<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
     
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  

</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      
<section class="cover">
    
      
      <a class="forkMe" href="https://github.com/Shen-Yu/hexo-theme-ayer"
        target="_blank"><img width="149" height="149" src="/images/forkme.png"
          class="attachment-full size-full" alt="Fork me on GitHub" data-recalc-dims="1"></a>
    
  <div class="cover-frame">
    <div class="bg-box">
      <img src="/images/cover1.jpg" alt="image frame" />
    </div>
    <div class="cover-inner text-center text-white">
      <h1><a href="/">Hexo</a></h1>
      <div id="subtitle-box">
        
        <span id="subtitle"></span>
        
      </div>
      <div>
        
      </div>
    </div>
  </div>
  <div class="cover-learn-more">
    <a href="javascript:void(0)" class="anchor"><i class="ri-arrow-down-line"></i></a>
  </div>
</section>



<script src="https://cdn.jsdelivr.net/npm/typed.js@2.0.11/lib/typed.min.js"></script>


<!-- Subtitle -->

  <script>
    try {
      var typed = new Typed("#subtitle", {
        strings: ['面朝大海，春暖花开', '何来天才，唯有苦练', '集中一点，登峰造极'],
        startDelay: 0,
        typeSpeed: 200,
        loop: true,
        backSpeed: 100,
        showCursor: true
      });
    } catch (err) {
      console.log(err)
    }
  </script>
  
<div id="main">
  <section class="outer">
  
  

<div class="notice" style="margin-top:50px">
    <i class="ri-heart-fill"></i>
    <div class="notice-content" id="broad"></div>
</div>
<script type="text/javascript">
    fetch('https://v1.hitokoto.cn')
        .then(response => response.json())
        .then(data => {
            document.getElementById("broad").innerHTML = data.hitokoto;
        })
        .catch(console.error)
</script>

<style>
    .notice {
        padding: 20px;
        border: 1px dashed #e6e6e6;
        color: #969696;
        position: relative;
        display: inline-block;
        width: 100%;
        background: #fbfbfb50;
        border-radius: 10px;
    }

    .notice i {
        float: left;
        color: #999;
        font-size: 16px;
        padding-right: 10px;
        vertical-align: middle;
        margin-top: -2px;
    }

    .notice-content {
        display: initial;
        vertical-align: middle;
    }
</style>
  
  <article class="articles">
    
    
    
    
    <article
  id="post-es/Elasticsearch 最佳实践"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/es/Elasticsearch%20%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/"
    >Elasticsearch 最佳实践.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/es/Elasticsearch%20%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/es/">es</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="Elasticsearch-最佳实践"><a href="#Elasticsearch-最佳实践" class="headerlink" title="Elasticsearch 最佳实践"></a>Elasticsearch 最佳实践</h1><p> 这里简单总结下ELK中Elasticsearch健康状态相关问题, Elasticsearch的索引状态和集群状态传达着不同的意思。</p>
<h2 id="一-Elasticsearch-集群健康状态"><a href="#一-Elasticsearch-集群健康状态" class="headerlink" title="一.  Elasticsearch 集群健康状态"></a>一.  Elasticsearch 集群健康状态</h2><p>一个 Elasticsearch 集群至少包括一个节点和一个索引。或者它 可能有一百个数据节点、三个单独的主节点，以及一小打客户端节点——这些共同操作一千个索引（以及上万个分片）。但是不管集群扩展到多大规模，你都会想要一个快速获取集群状态的途径。Cluster Health API 充当的就是这个角色。你可以把它想象成是在一万英尺的高度鸟瞰集群。它可以告诉你安心吧一切都好，或者警告你集群某个地方有问题。Elasticsearch 里其他 API 一样，cluster-health 会返回一个 JSON 响应。这对自动化和告警系统来说，非常便于解析。响应中包含了和你集群有关的一些关键信息:</p>
<p>查看Elasticsearch健康状态  (*表示ES集群的master主节点)</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# curl -XGET &#39;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_cat&#x2F;nodes?v&#39;  </span><br><span class="line">host      ip        heap.percent ram.percent load node.role master name                        </span><br><span class="line">10.0.8.47 10.0.8.47           53          85 0.16 d         *      elk-node03.kevin.cn  </span><br><span class="line">10.0.8.44 10.0.8.44           26          54 0.09 d         m      elk-node01.kevin.cn  </span><br><span class="line">10.0.8.45 10.0.8.45           71          81 0.02 d         m      elk-node02.kevin.cn  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>下面两条shell命令都可以监控到Elasticsearch健康状态</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# curl 10.0.8.47:9200&#x2F;_cat&#x2F;health  </span><br><span class="line">1554792912 14:55:12 kevin-elk green 3 3 4478 2239 0 0 0 0 - 100.0%  </span><br><span class="line">   </span><br><span class="line">[root@elk-node03 ~]# curl -X GET &#39;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_cluster&#x2F;health?pretty&#39;  </span><br><span class="line">&#123;  </span><br><span class="line">  &quot;cluster_name&quot; : &quot;kevin-elk&quot;,     #集群名称  </span><br><span class="line">  &quot;status&quot; : &quot;green&quot;,               #为 green 则代表健康没问题，如果是 yellow 或者 red 则是集群有问题  </span><br><span class="line">  &quot;timed_out&quot; : false,               #是否有超时  </span><br><span class="line">  &quot;number_of_nodes&quot; : 3,             #集群中的节点数量  </span><br><span class="line">  &quot;number_of_data_nodes&quot; : 3,  </span><br><span class="line">  &quot;active_primary_shards&quot; : 2234,  </span><br><span class="line">  &quot;active_shards&quot; : 4468,  </span><br><span class="line">  &quot;relocating_shards&quot; : 0,  </span><br><span class="line">  &quot;initializing_shards&quot; : 0,  </span><br><span class="line">  &quot;unassigned_shards&quot; : 0,  </span><br><span class="line">  &quot;delayed_unassigned_shards&quot; : 0,  </span><br><span class="line">  &quot;number_of_pending_tasks&quot; : 0,  </span><br><span class="line">  &quot;number_of_in_flight_fetch&quot; : 0,  </span><br><span class="line">  &quot;task_max_waiting_in_queue_millis&quot; : 0,  </span><br><span class="line">  &quot;active_shards_percent_as_number&quot; : 100.0      #集群分片的可用性百分比，如果为0则表示不可用  </span><br><span class="line">&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>正常情况下，Elasticsearch 集群健康状态分为三种：</p>
<ul>
<li><p>green:最健康得状态，说明所有的分片包括备份都可用; 这种情况Elasticsearch集群所有的主分片和副本分片都已分配, Elasticsearch集群是 100% 可用的。</p>
</li>
<li><p>yellow :基本的分片可用，但是备份不可用（或者是没有备份）;  这种情况Elasticsearch集群所有的主分片已经分片了，但至少还有一个副本是缺失的。不会有数据丢失，所以搜索结果依然是完整的。不过，你的高可用性在某种程度上被弱化。如果 更多的 分片消失，你就会丢数据了。把 yellow 想象成一个需要及时调查的警告。</p>
</li>
<li><p>red:部分的分片可用，表明分片有一部分损坏。此时执行查询部分数据仍然可以查到，遇到这种情况，还是赶快解决比较好; 这种情况Elasticsearch集群至少一个主分片（以及它的全部副本）都在缺失中。这意味着你在缺少数据：搜索只能返回部分数据，而分配到这个分片上的写入请求会返回一个异常。</p>
</li>
</ul>
<p>Elasticsearch 集群不健康时的排查思路</p>
<ul>
<li><p>首先确保 es 主节点最先启动，随后启动数据节点;</p>
</li>
<li><p>允许 selinux（非必要），关闭 iptables;</p>
</li>
<li><p>确保数据节点的elasticsearch配置文件正确;</p>
</li>
<li><p>系统最大打开文件描述符数是否够用;</p>
</li>
<li><p>elasticsearch设置的内存是否够用 (“ES_HEAP_SIZE”内存设置 和 “indices.fielddata.cache.size”上限设置);</p>
</li>
<li><p>elasticsearch的索引数量暴增 , 删除一部分索引(尤其是不需要的索引);</p>
</li>
</ul>
<h2 id="二-Elasticsearch索引状态"><a href="#二-Elasticsearch索引状态" class="headerlink" title="二.  Elasticsearch索引状态"></a>二.  Elasticsearch索引状态</h2><p>查看Elasticsearch 索引状态  (*表示ES集群的master主节点)</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# curl -XGET &#39;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_cat&#x2F;indices?v&#39;  </span><br><span class="line">health status index                                              pri rep docs.count docs.deleted store.size pri.store.size  </span><br><span class="line">green  open   10.0.61.24-vfc-intf-ent-deposit.log-2019.03.15       5   1        159            0    324.9kb        162.4kb  </span><br><span class="line">green  open   10.0.61.24-vfc-intf-ent-login.log-2019.03.04         5   1       3247            0      3.4mb          1.6mb  </span><br><span class="line">green  open   10.0.61.24-vfc-intf-ent-login.log-2019.03.05         5   1       1663            0      2.6mb          1.3mb  </span><br><span class="line">green  open   10.0.61.24-vfc-intf-ent-deposit.log-2019.03.19       5   1         14            0     81.1kb         40.5kb  </span><br><span class="line">.................  </span><br><span class="line">.................  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>Elasticsearch 索引的健康状态也有三种，即yellow、green、red与集群的健康状态解释是一样的!</p>
<h2 id="三-Elasticsearch-相关概念"><a href="#三-Elasticsearch-相关概念" class="headerlink" title="三.  Elasticsearch 相关概念"></a>三.  Elasticsearch 相关概念</h2><ul>
<li>Elasticsearch集群与节点</li>
</ul>
<p>节点(node)是你运行的Elasticsearch实例。一个集群(cluster)是一组具有相同cluster.name的节点集合，它们协同工作，共享数据并提供故障转移和扩展功能，当有新的节点加入或者删除节点，集群就会感知到并平衡数据。集群中一个节点会被选举为主节点(master),它用来管理集群中的一些变更，例如新建或删除索引、增加或移除节点等;当然一个节点也可以组成一个集群。</p>
<ul>
<li>Elasticsearch节点通信</li>
</ul>
<p>可以与集群中的任何节点通信，包括主节点。任何一个节点互相知道文档存在于哪个节点上，它们可以转发请求到我们需要数据所在的节点上。我们通信的节点负责收集各节点返回的数据，最后一起返回给客户端。这一切都由Elasticsearch透明的管理。</p>
<ul>
<li>Elasticsearch集群生态</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">1、同集群中节点之间可以扩容缩容;  </span><br><span class="line">2、主分片的数量会在其索引创建完成后修正，但是副本分片的数量会随时变化;   </span><br><span class="line">3、相同的分片不会放在同一个节点上;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li><p>Elasticsearch分片与副本分片 分片用于Elasticsearch在集群中分配数据, 可以想象把分片当作数据的容器, 文档存储在分片中，然后分片分配给你集群中的节点上。当集群扩容或缩小，Elasticsearch将会自动在节点间迁移分片，以使集群保持平衡。一个分片(shard)是一个最小级别的“工作单元(worker unit)”,它只是保存索引中所有数据的一小片.我们的文档存储和被索引在分片中，但是我们的程序不知道如何直接与它们通信。取而代之的是，它们直接与索引通信.Elasticsearch中的分片分为主分片和副本分片,复制分片只是主分片的一个副本，它用于提供数据的冗余副本，在硬件故障之后提供数据保护，同时服务于像搜索和检索等只读请求，主分片的数量和复制分片的数量都可以通过配置文件配置。但是主切片的数量只能在创建索引时定义且不能修改.相同的分片不会放在同一个节点上。</p>
</li>
<li><p>Elasticsearch分片算法</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">shard &#x3D; hash(routing) % number_of_primary_shards  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>routing值是一个任意字符串，它默认是_id但也可以自定义，这个routing字符串通过哈希函数生成一个数字，然后除以主切片的数量得到一个余数(remainder)，余数的范围永远是0到number_of_primary_shards - 1，这个数字就是特定文档所在的分片。这也解释了为什么主切片的数量只能在创建索引时定义且不能修改：如果主切片的数量在未来改变了，所有先前的路由值就失效了，文档也就永远找不到了。所有的文档API（get、index、delete、bulk、update、mget）都接收一个routing参数，它用来自定义文档到分片的映射。自定义路由值可以确保所有相关文档.比如用户的文章,按照用户账号路由,就可以实现属于同一用户的文档被保存在同一分片上。</p>
<ul>
<li>Elasticsearch分片与副本交互</li>
</ul>
<p>新建、索引和删除请求都是写(write)操作，它们必须在主分片上成功完成才能复制到相关的复制分片上,下面我们罗列在主分片和复制分片上成功新建、索引或删除一个文档必要的顺序步骤:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">1、客户端给Node 1发送新建、索引或删除请求。  </span><br><span class="line">2、节点使用文档的_id确定文档属于分片0。它转发请求到Node 3，分片0位于这个节点上。  </span><br><span class="line">3、Node 3在主分片上执行请求，如果成功，它转发请求到相应的位于Node 1和Node 2的复制节点上。当所有的复制节点报告成功，Node 3报告成功到请求的节点，请求的节点再报告给客户端。 客户端接收到成功响应的时候，文档的修改已经被应用于主分片和所有的复制分片。你的修改生效了  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>查看分片状态</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# curl -X GET &#39;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_cluster&#x2F;health?pretty&#39;  </span><br><span class="line">&#123;  </span><br><span class="line">  &quot;cluster_name&quot; : &quot;kevin-elk&quot;,  </span><br><span class="line">  &quot;status&quot; : &quot;green&quot;,  </span><br><span class="line">  &quot;timed_out&quot; : false,  </span><br><span class="line">  &quot;number_of_nodes&quot; : 3,  </span><br><span class="line">  &quot;number_of_data_nodes&quot; : 3,  </span><br><span class="line">  &quot;active_primary_shards&quot; : 2214,  </span><br><span class="line">  &quot;active_shards&quot; : 4428,  </span><br><span class="line">  &quot;relocating_shards&quot; : 0,  </span><br><span class="line">  &quot;initializing_shards&quot; : 0,  </span><br><span class="line">  &quot;unassigned_shards&quot; : 0,  </span><br><span class="line">  &quot;delayed_unassigned_shards&quot; : 0,  </span><br><span class="line">  &quot;number_of_pending_tasks&quot; : 0,  </span><br><span class="line">  &quot;number_of_in_flight_fetch&quot; : 0,  </span><br><span class="line">  &quot;task_max_waiting_in_queue_millis&quot; : 0,  </span><br><span class="line">  &quot;active_shards_percent_as_number&quot; : 100.0  </span><br><span class="line">&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>这里需要注意: 如下是单点单节点部署Elasticsearch, 集群状态可能为yellow, 因为单点部署Elasticsearch, 默认的分片副本数目配置为1，而相同的分片不能在一个节点上，所以就存在副本分片指定不明确的问题，所以显示为yellow，可以通过在Elasticsearch集群上添加一个节点来解决问题，如果不想这么做，可以删除那些指定不明确的副本分片（当然这不是一个好办法）但是作为测试和解决办法还是可以尝试的，下面试一下删除副本分片的办法:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-server ~]# curl -X GET &#39;http:&#x2F;&#x2F;localhost:9200&#x2F;_cluster&#x2F;health?pretty&#39;  </span><br><span class="line">&#123;  </span><br><span class="line">  &quot;cluster_name&quot; : &quot;elasticsearch&quot;,  </span><br><span class="line">  &quot;status&quot; : &quot;yellow&quot;,  </span><br><span class="line">  &quot;timed_out&quot; : false,  </span><br><span class="line">  &quot;number_of_nodes&quot; : 1,  </span><br><span class="line">  &quot;number_of_data_nodes&quot; : 1,  </span><br><span class="line">  &quot;active_primary_shards&quot; : 931,  </span><br><span class="line">  &quot;active_shards&quot; : 931,  </span><br><span class="line">  &quot;relocating_shards&quot; : 0,  </span><br><span class="line">  &quot;initializing_shards&quot; : 0,  </span><br><span class="line">  &quot;unassigned_shards&quot; : 930,  </span><br><span class="line">  &quot;delayed_unassigned_shards&quot; : 0,  </span><br><span class="line">  &quot;number_of_pending_tasks&quot; : 0,  </span><br><span class="line">  &quot;number_of_in_flight_fetch&quot; : 0,  </span><br><span class="line">  &quot;task_max_waiting_in_queue_millis&quot; : 0,  </span><br><span class="line">  &quot;active_shards_percent_as_number&quot; : 50.02686727565825  </span><br><span class="line">&#125;  </span><br><span class="line">   </span><br><span class="line">[root@elk-server ~]# curl -XPUT &quot;http:&#x2F;&#x2F;localhost:9200&#x2F;_settings&quot; -d&#39; &#123;  &quot;number_of_replicas&quot; : 0 &#125; &#39;  </span><br><span class="line">&#123;&quot;acknowledged&quot;:true&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>这个时候再次查看集群的状态状态变成了green</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-server ~]# curl -X GET &#39;http:&#x2F;&#x2F;localhost:9200&#x2F;_cluster&#x2F;health?pretty&#39;  </span><br><span class="line">&#123;  </span><br><span class="line">  &quot;cluster_name&quot; : &quot;elasticsearch&quot;,  </span><br><span class="line">  &quot;status&quot; : &quot;green&quot;,  </span><br><span class="line">  &quot;timed_out&quot; : false,  </span><br><span class="line">  &quot;number_of_nodes&quot; : 1,  </span><br><span class="line">  &quot;number_of_data_nodes&quot; : 1,  </span><br><span class="line">  &quot;active_primary_shards&quot; : 931,  </span><br><span class="line">  &quot;active_shards&quot; : 931,  </span><br><span class="line">  &quot;relocating_shards&quot; : 0,  </span><br><span class="line">  &quot;initializing_shards&quot; : 0,  </span><br><span class="line">  &quot;unassigned_shards&quot; : 0,  </span><br><span class="line">  &quot;delayed_unassigned_shards&quot; : 0,  </span><br><span class="line">  &quot;number_of_pending_tasks&quot; : 0,  </span><br><span class="line">  &quot;number_of_in_flight_fetch&quot; : 0,  </span><br><span class="line">  &quot;task_max_waiting_in_queue_millis&quot; : 0,  </span><br><span class="line">  &quot;active_shards_percent_as_number&quot; : 100.0  </span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure>

<ul>
<li>Elasticsearch索引的unssigned问题</li>
</ul>
<p>如下, 访问<a target="_blank" rel="noopener" href="http://10.0.8.47:9200//_plugin/head/">http://10.0.8.47:9200/\_plugin/head/</a>, 发现有unssigned现象:</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/tuSaKc6SfPqt6Tqwia3Famia3TBHgia8CQt8pgwfJicTkfALKldCVqXXKpDt06tRh4c00klAOsjzOkicA3WhTYTyUUA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1"></p>
<p>这里的unssigned就是未分配副本分片的问题，接下来执行settings中删除副本分片的命令后, 这个问题就解决了:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# curl -XPUT &quot;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_settings&quot; -d&#39; &#123;  &quot;number_of_replicas&quot; : 0 &#125; &#39;  </span><br><span class="line">&#123;&quot;acknowledged&quot;:true&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h2 id="四-Elasticsearch集群健康状态为”red”现象的排查分析"><a href="#四-Elasticsearch集群健康状态为”red”现象的排查分析" class="headerlink" title="四.  Elasticsearch集群健康状态为”red”现象的排查分析"></a>四.  Elasticsearch集群健康状态为”red”现象的排查分析</h2><p>通过Elasticsearch的Head插件访问, 发现Elasticsearch集群的健康值为red, 则说明至少一个主分片分配失败, 这将导致一些数据以及索引的某些部分不再可用。head插件会以不同的颜色显示, 绿色表示最健康的状态，代表所有的主分片和副本分片都可用；黄色表示所有的主分片可用，但是部分副本分片不可用；红色表示部分主分片不可用. (此时执行查询部分数据仍然可以查到，遇到这种情况，还是赶快解决比较好)</p>
<p>接着查看Elasticsearch启动日志会发现集群服务超时连接的情况:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">timeout notification from cluster service. timeout setting [1m], time since start [1m]  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>unassigned 分片问题可能的原因?</p>
<ul>
<li><p>INDEX_CREATED:  由于创建索引的API导致未分配。</p>
</li>
<li><p>CLUSTER_RECOVERED:  由于完全集群恢复导致未分配。</p>
</li>
<li><p>INDEX_REOPENED:  由于打开open或关闭close一个索引导致未分配。</p>
</li>
<li><p>DANGLING_INDEX_IMPORTED:  由于导入dangling索引的结果导致未分配。</p>
</li>
<li><p>NEW_INDEX_RESTORED:  由于恢复到新索引导致未分配。</p>
</li>
<li><p>EXISTING_INDEX_RESTORED:  由于恢复到已关闭的索引导致未分配。</p>
</li>
<li><p>REPLICA_ADDED:  由于显式添加副本分片导致未分配。</p>
</li>
<li><p>ALLOCATION_FAILED:  由于分片分配失败导致未分配。</p>
</li>
<li><p>NODE_LEFT:  由于承载该分片的节点离开集群导致未分配。</p>
</li>
<li><p>REINITIALIZED:  由于当分片从开始移动到初始化时导致未分配（例如，使用影子shadow副本分片）。</p>
</li>
<li><p>REROUTE_CANCELLED:  作为显式取消重新路由命令的结果取消分配。</p>
</li>
<li><p>REALLOCATED_REPLICA:  确定更好的副本位置被标定使用，导致现有的副本分配被取消，出现未分配。</p>
</li>
</ul>
<p>Elasticsearch集群状态红色如何排查？</p>
<ul>
<li><p>症状：集群健康值红色;</p>
</li>
<li><p>日志：集群服务连接超时；</p>
</li>
<li><p>可能原因：集群中部分节点的主分片未分配。</p>
</li>
</ul>
<p>接下来的解决方案主要围绕：使主分片unsigned 分片完成再分配展开。</p>
<p>如何解决 unassigned 分片问题？</p>
<ul>
<li>方案一：极端情况——这个分片数据已经不可用，直接删除该分片 (即删除索引) Elasticsearch中没有直接删除分片的接口，除非整个节点数据已不再使用，删除节点。</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">删除索引命令&quot;curl -XDELETE  http:&#x2F;&#x2F;10.0.8.44:9200&#x2F;索引名&quot;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>方案二：集群中节点数量 &gt;= 集群中所有索引的最大副本数量 +１</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">N &gt; &#x3D; R + 1  </span><br><span class="line">其中：  </span><br><span class="line">N——集群中节点的数目；  </span><br><span class="line">R——集群中所有索引的最大副本数目。  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>注意事项：当节点加入和离开集群时，主节点会自动重新分配分片，以确保分片的多个副本不会分配给同一个节点。换句话说，主节点不会将主分片分配给与其副本相同的节点，也不会将同一分片的两个副本分配给同一个节点。如果没有足够的节点相应地分配分片，则分片可能会处于未分配状态。</p>
<p>如果Elasticsearch集群就一个节点，即Ｎ＝１；所以Ｒ＝０，才能满足公式。这样问题就转嫁为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">1) 添加节点处理，即Ｎ增大；  </span><br><span class="line">2) 删除副本分片，即R置为0。  </span><br><span class="line">  </span><br><span class="line">#R置为0的方式，可以通过如下命令行实现:  </span><br><span class="line">[root@elk-node03 ~]# curl -XPUT &quot;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_settings&quot; -d&#39; &#123;  &quot;number_of_replicas&quot; : 0 &#125; &#39;  </span><br><span class="line">&#123;&quot;acknowledged&quot;:true&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>方案三：allocate重新分配分片</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">如果方案二仍然未解决，可以考虑重新分配分片。可能的原因：  </span><br><span class="line">1) 节点在重新启动时可能遇到问题。正常情况下，当一个节点恢复与群集的连接时，它会将有关其分片的信息转发给主节点，然后主节点将这分片从“未分配”转换为 &quot;已分配&#x2F;已启动&quot;。  </span><br><span class="line">2) 当由于某种原因 (例如节点的存储已被损坏) 导致该进程失败时，分片可能保持未分配状态。  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>在这种情况下，必须决定如何继续: 尝试让原始节点恢复并重新加入集群(并且不要强制分配主分片);  或者强制使用Reroute API分配分片并重新索引缺少的数据原始数据源或备份。如果你决定分配未分配的主分片，请确保将”allow_primary”：”true”标志添加到请求中。</p>
<p>Elasticsearch5.X使用脚本如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">#!&#x2F;bin&#x2F;bash  </span><br><span class="line">NODE&#x3D;&quot;YOUR NODE NAME&quot;  </span><br><span class="line">IFS&#x3D;$&#39;\n&#39;  </span><br><span class="line">for line in $(curl -s &#39;10.0.8.47:9200&#x2F;_cat&#x2F;shards&#39; | fgrep UNASSIGNED); do  </span><br><span class="line">  INDEX&#x3D;$(echo $line | (awk &#39;&#123;print $1&#125;&#39;))  </span><br><span class="line">  SHARD&#x3D;$(echo $line | (awk &#39;&#123;print $2&#125;&#39;))  </span><br><span class="line">   </span><br><span class="line">  curl -XPOST &#39;10.0.8.47:9200&#x2F;_cluster&#x2F;reroute&#39; -d &#39;&#123;  </span><br><span class="line">     &quot;commands&quot;: [  </span><br><span class="line">        &#123;  </span><br><span class="line">            &quot; allocate_replica &quot;: &#123;  </span><br><span class="line">                &quot;index&quot;: &quot;&#39;$INDEX&#39;&quot;,  </span><br><span class="line">                &quot;shard&quot;: &#39;$SHARD&#39;,  </span><br><span class="line">                &quot;node&quot;: &quot;&#39;$NODE&#39;&quot;,  </span><br><span class="line">                &quot;allow_primary&quot;: true  </span><br><span class="line">          &#125;  </span><br><span class="line">        &#125;  </span><br><span class="line">    ]  </span><br><span class="line">  &#125;&#39;  </span><br><span class="line">done  </span><br><span class="line">#Elasticsearch2.X及早期版本，只需将上面脚本中的allocate_replica改为 allocate，其他不变。  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h2 id="五-案例-ELK中ElasticSearch集群状态异常问题"><a href="#五-案例-ELK中ElasticSearch集群状态异常问题" class="headerlink" title="五.  案例: ELK中ElasticSearch集群状态异常问题"></a>五.  案例: ELK中ElasticSearch集群状态异常问题</h2><p>线上环境部署的ELK日志集中分析系统, 过了一段时间后, 发现Kibana展示里没有日志, 查看head插件索引情况, 发现一直打不开! 这是因为如果不对es索引定期做处理, 则随着日志收集数据量的不断增大, es内存消耗不断增量, 索引数量也会随之暴增, 那么elk就会出现问题, 比如elk页面展示超时, 访问<a target="_blank" rel="noopener" href="http://10.0.8.47:9200//_plugin/head/">http://10.0.8.47:9200/\_plugin/head/</a> 一直卡顿等; es集群状态异常(出现red的status)等!</p>
<p>在任意一个node节点上执行下面命令查看es集群状态 (url里的ip地址可以是三个node中的任意一个), 如下可知, es集群当前master节点是10.0.8.47</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# curl -XGET &#39;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_cat&#x2F;nodes?v&#39;  </span><br><span class="line">host      ip        heap.percent ram.percent load node.role master name                         </span><br><span class="line">10.0.8.47 10.0.8.47           31          78 0.92 d         *      elk-node03.kevin.cn  </span><br><span class="line">10.0.8.44 10.0.8.44           16          55 0.27 d         m      elk-node01.kevin.cn  </span><br><span class="line">10.0.8.45 10.0.8.45           61          78 0.11 d         m      elk-node02.kevin.cn  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>查询集群的健康状态（一共三种状态：green、yellow，red；其中green表示健康）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# curl -XGET &#39;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_cat&#x2F;health?v&#39;  </span><br><span class="line">epoch      timestamp cluster  status node.total node.data shards  pri relo init unassign pending_tasks max_task_wait_time active_shards_percent  </span><br><span class="line">1554689492 10:11:32  kevin-elk red             3         3   3587 3447    0    6     5555           567              11.1m                 39.2%  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>解决办法:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">1) 调优集群的稳定性  </span><br><span class="line">-&gt; 增大系统最大打开文件描述符数，即65535;  </span><br><span class="line">-&gt; 关闭swap，锁定进程地址空间，防止内存swap;  </span><br><span class="line">-&gt; JVM调优, 增大es内存设置, 默认是2g (Heap Size不超过物理内存的一半，且小于32G);  </span><br><span class="line">2) 定期删除es索引或删除不可用的索引, 比如只保留最近一个月的索引数据 (可写脚本定期执行, 具体可参考: https:&#x2F;&#x2F;www.cnblogs.com&#x2F;kevingrace&#x2F;p&#x2F;9994178.html);  </span><br><span class="line">3) 如果es主节点重启, 则主节点在转移到其他节点过程中, 分片分片也会转移过去; 如果分片比较多, 数据量比较大, 则需要耗费一定的时间, 在此过程中, elk集群的状态是yellow; 查看elk集群状态, shards分片会不断增加, unassign会不断减少,直至unassign减到0时, 表明分片已经完全转移到新的主节点上, 则此时查看elk的健康状态就是green了;  </span><br><span class="line">4) 如果所有es节点都重启, 则需要先启动一个节点作为master主节点, 然后再启动其他节点;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>注意, 这里记录下修改ElasticSearch的内存配置操作 (“ES_HEAP_SIZE”内存设置 和 “indices.fielddata.cache.size”上限设置)</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">先修改&#x2F;etc&#x2F;sysconfig&#x2F;elasticsearch 文件里的ES_HEAP_SIZE参数值, 默认为2g  </span><br><span class="line">[root@elk-node03 ~]# vim &#x2F;etc&#x2F;sysconfig&#x2F;elasticsearch  </span><br><span class="line">.............  </span><br><span class="line">ES_HEAP_SIZE&#x3D;8g </span><br></pre></td></tr></table></figure>

<p>接着修改elasticsearch配置文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# vim &#x2F;etc&#x2F;elasticsearch&#x2F;elasticsearch.yml  </span><br><span class="line">.............  </span><br><span class="line">bootstrap.mlockall: true     #默认为false. 表示锁住内存.当JVM进行内存转换时,es性能会降低, 设置此参数值为true即可锁住内存.  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>注意: 这个时候最好在elasticsearch.yml配置文件里设置下indices.fielddata.cache.size , 此参数表示”控制有多少堆内存是分配给fielddata” 因为elasticsearch在查询时，fielddata缓存的数据越来越多造成的（默认是不自动清理的）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# vim &#x2F;etc&#x2F;elasticsearch&#x2F;elasticsearch.yml  </span><br><span class="line">..............  </span><br><span class="line">indices.fielddata.cache.size: 40%  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>上面设置了限制fielddata 上限, 表示让字段数据缓存的内存大小达到heap 40% (也就是上面设置的8g的40%)的时候就起用自动清理旧的缓存数据</p>
<p>然后重启elasticsearch</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# systemctl restart elasticsearch  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>查看启动的elasticsearch, 发现内存已经调整到8g了</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# ps -ef|grep elasticsearch  </span><br><span class="line">root      7066  3032  0 16:46 pts&#x2F;0    00:00:00 grep --color&#x3D;auto elasticsearch  </span><br><span class="line">elastic+ 15586     1 22 10:33 ?        01:22:00 &#x2F;bin&#x2F;java -Xms8g -Xmx8g -Djava.awt.headless&#x3D;true -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction&#x3D;75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+HeapDumpOnOutOfMemoryError -XX:+DisableExplicitGC -Dfile.encoding&#x3D;UTF-8 -Djna.nosys&#x3D;true -Des.path.home&#x3D;&#x2F;usr&#x2F;share&#x2F;elasticsearch -cp &#x2F;usr&#x2F;share&#x2F;elasticsearch&#x2F;lib&#x2F;elasticsearch-2.4.6.jar:&#x2F;usr&#x2F;share&#x2F;elasticsearch&#x2F;lib&#x2F;* org.elasticsearch.bootstrap.Elasticsearch start -Des.pidfile&#x3D;&#x2F;var&#x2F;run&#x2F;elasticsearch&#x2F;elasticsearch.pid -Des.default.path.home&#x3D;&#x2F;usr&#x2F;share&#x2F;elasticsearch -Des.default.path.logs&#x3D;&#x2F;var&#x2F;log&#x2F;elasticsearch -Des.default.path.data&#x3D;&#x2F;var&#x2F;lib&#x2F;elasticsearch -Des.default.path.conf&#x3D;&#x2F;etc&#x2F;elasticsearch </span><br></pre></td></tr></table></figure>

<p>如上, 在进行一系列修复操作 (增大系统最大打开文件描述符数65535, 关闭swap，锁定进程地址空间，防止内存swap, 增大ES内存, 删除不用或异常索引, 重启各节点的ES服务) 后, 再次查看ES集群状态, 发现此时仍然是”red”状态. 这是因为es主节点重启, 则主节点在转移到其他节点过程中, 分片分片也会转移过去; 如果分片比较多, 数据量比较大, 则需要耗费一定的时间. 需要等到unassign减到0时, 表明分片已经完全转移到新的主节点上, 则此时查看elk的健康状态就是green了.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node02 system]# curl -XGET &#39;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_cat&#x2F;health?v&#39;  </span><br><span class="line">epoch      timestamp cluster  status node.total node.data shards  pri relo init unassign pending_tasks max_task_wait_time active_shards_percent  </span><br><span class="line">1554691187 10:39:47  kevin-elk red             3         3   4460 3878    0    8     4660           935               5.7m                 48.9%  </span><br><span class="line">   </span><br><span class="line">[root@elk-node02 system]# curl -XGET &#39;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_cat&#x2F;health?v&#39;  </span><br><span class="line">epoch      timestamp cluster  status node.total node.data shards  pri relo init unassign pending_tasks max_task_wait_time active_shards_percent  </span><br><span class="line">1554691187 10:39:47  kevin-elk red             3         3   4466 3882    0    8     4654           944               5.7m                 48.9%  </span><br><span class="line">   </span><br><span class="line">................  </span><br><span class="line">................  </span><br><span class="line">   </span><br><span class="line">#等到&quot;unassign&quot;数值为0时, 再次查看es状态  </span><br><span class="line">[root@elk-node03 ~]# curl -XGET &#39;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_cat&#x2F;health?v&#39;  </span><br><span class="line">epoch      timestamp cluster  status node.total node.data shards  pri relo init unassign pending_tasks max_task_wait_time active_shards_percent  </span><br><span class="line">1554692772 11:06:12  kevin-elk green           3         3   9118 4559    0    0        0             0                  -                100.0%  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>如果es状态此时还是red, 则需要找出red状态的索引并且删除 (这个时候的red状态的索引应该是少部分)</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node02 system]# curl -XGET   &quot;http:&#x2F;&#x2F;10.0.8.45:9200&#x2F;_cat&#x2F;indices?v&quot;|grep -w &quot;red&quot;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>比如找出的red状态的索引名为”10.0.61.24-vfc-intf-ent-order.log-2019.03.04”, 删除它即可</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node02 system]# curl -XDELETE  http:&#x2F;&#x2F;10.0.8.44:9200&#x2F;10.0.61.24-vfc-intf-ent-order.log-2019.03.04  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>需要特别注意:  如果elasticSearch集群节点中es数据所在的磁盘使用率超过了一定比例(比如85%), 则就会出现无法再为副分片分片的情况, 这也会导致elasticSearch集群监控状态也会出现”red”情况!!!  这个时候只需要增大这块磁盘的空间, 磁盘空间够用了, elasticSearch就会自动恢复数据!!!</p>
<h2 id="六-Elasticsearch常见错误"><a href="#六-Elasticsearch常见错误" class="headerlink" title="六.  Elasticsearch常见错误"></a>六.  Elasticsearch常见错误</h2><ul>
<li>错误1: Exception in thread “main” SettingsException[Failed to load settings from [elasticsearch.yml]]; nested: ElasticsearchParseException[malformed, expected settings to start with ‘object’, instead was [VALUE_STRING]];</li>
</ul>
<p>原因：elasticsearch.yml文件配置错误导致</p>
<p>解决：参数与参数值(等号)间需要空格</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# vim &#x2F;etc&#x2F;elasticsearch&#x2F;elasticsearch.yml  </span><br><span class="line">...............  </span><br><span class="line">#node.name:elk-node03.kevin.cn         #错误  </span><br><span class="line">node.name: elk-node03.kevin.cn           #正确  </span><br><span class="line">   </span><br><span class="line">#或者如下配置  </span><br><span class="line">#node.name &#x3D;&quot;elk-node03.kevin.cn&quot;    #错误  </span><br><span class="line">#node.name &#x3D; &quot;elk-node03.kevin.cn&quot;   #正确  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>然后重启elasticsearch服务</p>
<ul>
<li>错误2: org.elasticsearch.bootstrap.StartupException: java.lang.RuntimeException: can not run elasticsearch as root</li>
</ul>
<p>原因：处于对root用户的安全保护，需要使用其他用户组进行授权启动</p>
<p>解决：用户组进行授权启动</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# groupadd elasticsearch            </span><br><span class="line">[root@elk-node03 ~]# useradd elasticsearch -g elasticsearch -p elasticsearch  </span><br><span class="line">[root@elk-node03 ~]# chown -R elasticsearch.elasticsearch &#x2F;data&#x2F;es-data                 #给es的数据目录授权, 否则es服务启动报错  </span><br><span class="line">[root@elk-node03 ~]# chown -R elasticsearch.elasticsearch&#x2F;var&#x2F;log&#x2F;elasticsearch     #给es的日志目录授权, 否则es服务启动报错  </span><br><span class="line">   </span><br><span class="line">#以上是yum安装elasticsearch情况, 需要给elasticsearch的数据目录和日志目录授权, 如果elasticsearch是编译安装, 则需要给它的安装目录也授权  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>接着重启elasticsearch服务即可</p>
<ul>
<li>错误3: OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000085330000, 2060255232, 0) failed; error=’Cannot a …’(errno=12);</li>
</ul>
<p>原因：jvm要分配最大内存超出系统内存</p>
<p>解决：适当调整指定jvm内存, 编辑elasticsearch 的jvm配置文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># vim &#x2F;data&#x2F;elasticsearch&#x2F;config&#x2F;jvm.options  </span><br><span class="line">-Xms8g  </span><br><span class="line">-Xmx8g  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>如果是yum安装的elasticsearch, 则修改如下配置文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# vim &#x2F;etc&#x2F;sysconfig&#x2F;elasticsearch  </span><br><span class="line"># Heap size defaults to 256m min, 1g max             #最小为1g  </span><br><span class="line"># Set ES_HEAP_SIZE to 50% of available RAM, but no more than 31g     #设置为物理内存的50%, 但不要操作31g  </span><br><span class="line">ES_HEAP_SIZE&#x3D;8g  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>然后重启elasticsearch服务即可</p>
<ul>
<li>错误4: ERROR: [3] bootstrap checks failed</li>
</ul>
<p>原因：虚拟机限制用户的执行内存</p>
<p>解决：修改安全限制配置文件 (使用root最高权限 修改安全配置 在文件末尾加入)</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# vim &#x2F;etc&#x2F;security&#x2F;limits.conf  </span><br><span class="line">elasticsearch       hard        nofile        65536  </span><br><span class="line">elasticsearch       soft        nofile        65536  </span><br><span class="line">*               soft       nproc         4096  </span><br><span class="line">*               hard      nproc          4096  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>修改系统配置文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[3]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]  </span><br><span class="line">   </span><br><span class="line">[root@elk-node03 ~]# &#x2F;etc&#x2F;sysctl.conf        #注意下面的参数值大于错误提示值  </span><br><span class="line">vm.max_map_count &#x3D; 655360  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>然后重启elasticsearch服务即可</p>
<h2 id="七-Elasticsearch集群监控状态监控"><a href="#七-Elasticsearch集群监控状态监控" class="headerlink" title="七.  Elasticsearch集群监控状态监控"></a>七.  Elasticsearch集群监控状态监控</h2><ul>
<li></li>
</ul>
<ol>
<li>通过简单shell命令监控elasticsearch集群状态</li>
</ol>
<p>原理：使用curl命令模拟访问任意一个elasticsearch集群, 就可以反馈出elasticsearch集群状态，集群的状态需要为green。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# curl -XGET &#39;http:&#x2F;&#x2F;10.0.8.47:9200&#x2F;_cluster&#x2F;stats?human&amp;pretty&#39;  </span><br><span class="line">&#123;  </span><br><span class="line">  &quot;timestamp&quot; : 1554792101956,  </span><br><span class="line">  &quot;cluster_name&quot; : &quot;kevin-elk&quot;,  </span><br><span class="line">  &quot;status&quot; : &quot;green&quot;,  </span><br><span class="line">  &quot;indices&quot; : &#123;  </span><br><span class="line">    &quot;count&quot; : 451,  </span><br><span class="line">    &quot;shards&quot; : &#123;  </span><br><span class="line">      &quot;total&quot; : 4478,  </span><br><span class="line">      &quot;primaries&quot; : 2239,  </span><br><span class="line">      &quot;replication&quot; : 1.0,  </span><br><span class="line">      &quot;index&quot; : &#123;  </span><br><span class="line">        &quot;shards&quot; : &#123;  </span><br><span class="line">          &quot;min&quot; : 2,  </span><br><span class="line">          &quot;max&quot; : 10,  </span><br><span class="line">          &quot;avg&quot; : 9.929046563192905  </span><br><span class="line">        &#125;,  </span><br><span class="line">        &quot;primaries&quot; : &#123;  </span><br><span class="line">          &quot;min&quot; : 1,  </span><br><span class="line">          &quot;max&quot; : 5,  </span><br><span class="line">          &quot;avg&quot; : 4.964523281596453  </span><br><span class="line">        &#125;,  </span><br><span class="line">        &quot;replication&quot; : &#123;  </span><br><span class="line">          &quot;min&quot; : 1.0,  </span><br><span class="line">          &quot;max&quot; : 1.0,  </span><br><span class="line">          &quot;avg&quot; : 1.0  </span><br><span class="line">        &#125;  </span><br><span class="line">      &#125;  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;docs&quot; : &#123;  </span><br><span class="line">      &quot;count&quot; : 10448854,  </span><br><span class="line">      &quot;deleted&quot; : 3  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;store&quot; : &#123;  </span><br><span class="line">      &quot;size&quot; : &quot;5gb&quot;,  </span><br><span class="line">      &quot;size_in_bytes&quot; : 5467367887,  </span><br><span class="line">      &quot;throttle_time&quot; : &quot;0s&quot;,  </span><br><span class="line">      &quot;throttle_time_in_millis&quot; : 0  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;fielddata&quot; : &#123;  </span><br><span class="line">      &quot;memory_size&quot; : &quot;0b&quot;,  </span><br><span class="line">      &quot;memory_size_in_bytes&quot; : 0,  </span><br><span class="line">      &quot;evictions&quot; : 0  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;query_cache&quot; : &#123;  </span><br><span class="line">      &quot;memory_size&quot; : &quot;0b&quot;,  </span><br><span class="line">      &quot;memory_size_in_bytes&quot; : 0,  </span><br><span class="line">      &quot;total_count&quot; : 364053,  </span><br><span class="line">      &quot;hit_count&quot; : 0,  </span><br><span class="line">      &quot;miss_count&quot; : 364053,  </span><br><span class="line">      &quot;cache_size&quot; : 0,  </span><br><span class="line">      &quot;cache_count&quot; : 0,  </span><br><span class="line">      &quot;evictions&quot; : 0  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;completion&quot; : &#123;  </span><br><span class="line">      &quot;size&quot; : &quot;0b&quot;,  </span><br><span class="line">      &quot;size_in_bytes&quot; : 0  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;segments&quot; : &#123;  </span><br><span class="line">      &quot;count&quot; : 16635,  </span><br><span class="line">      &quot;memory&quot; : &quot;83.6mb&quot;,  </span><br><span class="line">      &quot;memory_in_bytes&quot; : 87662804,  </span><br><span class="line">      &quot;terms_memory&quot; : &quot;64.5mb&quot;,  </span><br><span class="line">      &quot;terms_memory_in_bytes&quot; : 67635408,  </span><br><span class="line">      &quot;stored_fields_memory&quot; : &quot;6.3mb&quot;,  </span><br><span class="line">      &quot;stored_fields_memory_in_bytes&quot; : 6624464,  </span><br><span class="line">      &quot;term_vectors_memory&quot; : &quot;0b&quot;,  </span><br><span class="line">      &quot;term_vectors_memory_in_bytes&quot; : 0,  </span><br><span class="line">      &quot;norms_memory&quot; : &quot;6.1mb&quot;,  </span><br><span class="line">      &quot;norms_memory_in_bytes&quot; : 6478656,  </span><br><span class="line">      &quot;doc_values_memory&quot; : &quot;6.6mb&quot;,  </span><br><span class="line">      &quot;doc_values_memory_in_bytes&quot; : 6924276,  </span><br><span class="line">      &quot;index_writer_memory&quot; : &quot;448.1kb&quot;,  </span><br><span class="line">      &quot;index_writer_memory_in_bytes&quot; : 458896,  </span><br><span class="line">      &quot;index_writer_max_memory&quot; : &quot;4.5gb&quot;,  </span><br><span class="line">      &quot;index_writer_max_memory_in_bytes&quot; : 4914063972,  </span><br><span class="line">      &quot;version_map_memory&quot; : &quot;338b&quot;,  </span><br><span class="line">      &quot;version_map_memory_in_bytes&quot; : 338,  </span><br><span class="line">      &quot;fixed_bit_set&quot; : &quot;0b&quot;,  </span><br><span class="line">      &quot;fixed_bit_set_memory_in_bytes&quot; : 0  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;percolate&quot; : &#123;  </span><br><span class="line">      &quot;total&quot; : 0,  </span><br><span class="line">      &quot;time&quot; : &quot;0s&quot;,  </span><br><span class="line">      &quot;time_in_millis&quot; : 0,  </span><br><span class="line">      &quot;current&quot; : 0,  </span><br><span class="line">      &quot;memory_size_in_bytes&quot; : -1,  </span><br><span class="line">      &quot;memory_size&quot; : &quot;-1b&quot;,  </span><br><span class="line">      &quot;queries&quot; : 0  </span><br><span class="line">    &#125;  </span><br><span class="line">  &#125;,  </span><br><span class="line">  &quot;nodes&quot; : &#123;  </span><br><span class="line">    &quot;count&quot; : &#123;  </span><br><span class="line">      &quot;total&quot; : 3,  </span><br><span class="line">      &quot;master_only&quot; : 0,  </span><br><span class="line">      &quot;data_only&quot; : 0,  </span><br><span class="line">      &quot;master_data&quot; : 3,  </span><br><span class="line">      &quot;client&quot; : 0  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;versions&quot; : [ &quot;2.4.6&quot; ],  </span><br><span class="line">    &quot;os&quot; : &#123;  </span><br><span class="line">      &quot;available_processors&quot; : 24,  </span><br><span class="line">      &quot;allocated_processors&quot; : 24,  </span><br><span class="line">      &quot;mem&quot; : &#123;  </span><br><span class="line">        &quot;total&quot; : &quot;13.8gb&quot;,  </span><br><span class="line">        &quot;total_in_bytes&quot; : 14859091968  </span><br><span class="line">      &#125;,  </span><br><span class="line">      &quot;names&quot; : [ &#123;  </span><br><span class="line">        &quot;name&quot; : &quot;Linux&quot;,  </span><br><span class="line">        &quot;count&quot; : 3  </span><br><span class="line">      &#125; ]  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;process&quot; : &#123;  </span><br><span class="line">      &quot;cpu&quot; : &#123;  </span><br><span class="line">        &quot;percent&quot; : 1  </span><br><span class="line">      &#125;,  </span><br><span class="line">      &quot;open_file_descriptors&quot; : &#123;  </span><br><span class="line">        &quot;min&quot; : 9817,  </span><br><span class="line">        &quot;max&quot; : 9920,  </span><br><span class="line">        &quot;avg&quot; : 9866  </span><br><span class="line">      &#125;  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;jvm&quot; : &#123;  </span><br><span class="line">      &quot;max_uptime&quot; : &quot;1.1d&quot;,  </span><br><span class="line">      &quot;max_uptime_in_millis&quot; : 101282315,  </span><br><span class="line">      &quot;versions&quot; : [ &#123;  </span><br><span class="line">        &quot;version&quot; : &quot;1.8.0_131&quot;,  </span><br><span class="line">        &quot;vm_name&quot; : &quot;Java HotSpot(TM) 64-Bit Server VM&quot;,  </span><br><span class="line">        &quot;vm_version&quot; : &quot;25.131-b11&quot;,  </span><br><span class="line">        &quot;vm_vendor&quot; : &quot;Oracle Corporation&quot;,  </span><br><span class="line">        &quot;count&quot; : 3  </span><br><span class="line">      &#125; ],  </span><br><span class="line">      &quot;mem&quot; : &#123;  </span><br><span class="line">        &quot;heap_used&quot; : &quot;7.2gb&quot;,  </span><br><span class="line">        &quot;heap_used_in_bytes&quot; : 7800334800,  </span><br><span class="line">        &quot;heap_max&quot; : &quot;23.8gb&quot;,  </span><br><span class="line">        &quot;heap_max_in_bytes&quot; : 25560612864  </span><br><span class="line">      &#125;,  </span><br><span class="line">      &quot;threads&quot; : 359  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;fs&quot; : &#123;  </span><br><span class="line">      &quot;total&quot; : &quot;1.1tb&quot;,  </span><br><span class="line">      &quot;total_in_bytes&quot; : 1241247670272,  </span><br><span class="line">      &quot;free&quot; : &quot;1tb&quot;,  </span><br><span class="line">      &quot;free_in_bytes&quot; : 1206666141696,  </span><br><span class="line">      &quot;available&quot; : &quot;1tb&quot;,  </span><br><span class="line">      &quot;available_in_bytes&quot; : 1143543336960  </span><br><span class="line">    &#125;,  </span><br><span class="line">    &quot;plugins&quot; : [ &#123;  </span><br><span class="line">      &quot;name&quot; : &quot;bigdesk&quot;,  </span><br><span class="line">      &quot;version&quot; : &quot;master&quot;,  </span><br><span class="line">      &quot;description&quot; : &quot;bigdesk -- Live charts and statistics for Elasticsearch cluster &quot;,  </span><br><span class="line">      &quot;url&quot; : &quot;&#x2F;_plugin&#x2F;bigdesk&#x2F;&quot;,  </span><br><span class="line">      &quot;jvm&quot; : false,  </span><br><span class="line">      &quot;site&quot; : true  </span><br><span class="line">    &#125;, &#123;  </span><br><span class="line">      &quot;name&quot; : &quot;head&quot;,  </span><br><span class="line">      &quot;version&quot; : &quot;master&quot;,  </span><br><span class="line">      &quot;description&quot; : &quot;head - A web front end for an elastic search cluster&quot;,  </span><br><span class="line">      &quot;url&quot; : &quot;&#x2F;_plugin&#x2F;head&#x2F;&quot;,  </span><br><span class="line">      &quot;jvm&quot; : false,  </span><br><span class="line">      &quot;site&quot; : true  </span><br><span class="line">    &#125;, &#123;  </span><br><span class="line">      &quot;name&quot; : &quot;kopf&quot;,  </span><br><span class="line">      &quot;version&quot; : &quot;2.0.1&quot;,  </span><br><span class="line">      &quot;description&quot; : &quot;kopf - simple web administration tool for Elasticsearch&quot;,  </span><br><span class="line">      &quot;url&quot; : &quot;&#x2F;_plugin&#x2F;kopf&#x2F;&quot;,  </span><br><span class="line">      &quot;jvm&quot; : false,  </span><br><span class="line">      &quot;site&quot; : true  </span><br><span class="line">    &#125; ]  </span><br><span class="line">  &#125;  </span><br><span class="line">&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>以上监控命令打印的集群统计信息包含: Elasticsearch集群的分片数，文档数，存储空间，缓存信息，内存作用率，插件内容，文件系统内容，JVM 作用状况，系统 CPU，OS 信息，段信息。</p>
<ul>
<li></li>
</ul>
<ol start="2">
<li>利用脚本监控elasticSearch集群健康值green yellow red状态</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# curl 10.0.8.47:9200&#x2F;_cat&#x2F;health  </span><br><span class="line">1554864073 10:41:13 qwkg-elk green 3 3 4478 2239 0 0 0 0 - 100.0%  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>编写python脚本, 监控elasticsearch的健康状态</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# vim &#x2F;opt&#x2F;es_health_monit.py     </span><br><span class="line">import commands  </span><br><span class="line">command &#x3D; &#39;curl 10.0.8.47:9200&#x2F;_cat&#x2F;health&#39;  </span><br><span class="line">(a, b) &#x3D; commands.getstatusoutput(command)  </span><br><span class="line">status&#x3D; b.split(&#39; &#39;)[157]  </span><br><span class="line">if status&#x3D;&#x3D;&#39;red&#39;:  </span><br><span class="line">    healthy&#x3D;0  </span><br><span class="line">else:  </span><br><span class="line">    healthy&#x3D;1  </span><br><span class="line">    </span><br><span class="line">print healthy  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>手动执行脚本, 打印出elasticsearch健康状态</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@elk-node03 ~]# chmod 755 &#x2F;opt&#x2F;es_health_monit.py  </span><br><span class="line">[root@elk-node03 ~]# python &#x2F;opt&#x2F;es_health_monit.py  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>然后在脚本中结合sendemail进行邮件报警 或者 添加到zabbix监控里.</p>
<h2 id="八-Elasticsearch配置中防止脑裂的配置"><a href="#八-Elasticsearch配置中防止脑裂的配置" class="headerlink" title="八.  Elasticsearch配置中防止脑裂的配置"></a>八.  Elasticsearch配置中防止脑裂的配置</h2><p>Master和DataNode未分离，导致集群不稳定。</p>
<p>在ES集群中，节点分为Master、DataNode、Client等几种角色，任何一个节点都可以同时具备以上所有角色，其中比较重要的角色为Master和DataNode:</p>
<ul>
<li></li>
</ul>
<ol>
<li>Master主要管理集群信息、primary分片和replica分片信息、维护index信息。</li>
</ol>
<ul>
<li></li>
</ul>
<ol start="2">
<li>DataNode用来存储数据，维护倒排索引，提供数据检索等。</li>
</ol>
<p>可以看到元信息都在Master上面，如果Master挂掉了，该Master含有的所有Index都无法访问，文档中说，为了保证Master稳定，需要将Master和Node分离。而构建master集群可能会产生一种叫做脑裂的问题，为了防止脑裂，需要设置最小master的节点数为eligible_master_number/2 + 1</p>
<p>脑裂的概念：如果有两个Master候选节点，并设置最小Master节点数为1，则当网络抖动或偶然断开时，两个Master都会认为另一个Master挂掉了，它们都被选举为主Master，则此时集群中存在两个主Master，即物理上一个集群变成了逻辑上的两个集群，而当其中一个Master再次挂掉时，即便它恢复后回到了原有的集群，在它作为主Master期间写入的数据都会丢失，因为它上面维护了Index信息。</p>
<p>根据以上理论，可以对集群做了如下更改，额外选取三个独立的机器作为Master节点，修改elasticsearch.yml配置</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">node.master &#x3D; true  </span><br><span class="line">node.data &#x3D; false  </span><br><span class="line">discovery.zen.minimum_master_nodes &#x3D; 2  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>修改其他节点配置，将其设置为DataNode，最后依次重启</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">node.master &#x3D; false  </span><br><span class="line">node.data &#x3D; true  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<blockquote>
<p>作者：散尽浮华</p>
<p>原文：<a target="_blank" rel="noopener" href="http://www.cnblogs.com/kevingrace/p/10671063.html">www.cnblogs.com/kevingrace/p/10671063.html</a></p>
</blockquote>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/es/" rel="tag">es</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-interview/IT运维面试问题总结-数据库、监控、网络管理"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/interview/IT%E8%BF%90%E7%BB%B4%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93-%E6%95%B0%E6%8D%AE%E5%BA%93%E3%80%81%E7%9B%91%E6%8E%A7%E3%80%81%E7%BD%91%E7%BB%9C%E7%AE%A1%E7%90%86/"
    >IT运维面试问题总结-数据库、监控、网络管理.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/interview/IT%E8%BF%90%E7%BB%B4%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93-%E6%95%B0%E6%8D%AE%E5%BA%93%E3%80%81%E7%9B%91%E6%8E%A7%E3%80%81%E7%BD%91%E7%BB%9C%E7%AE%A1%E7%90%86/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/interview/">interview</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="IT运维面试问题总结-数据库、监控、网络管理"><a href="#IT运维面试问题总结-数据库、监控、网络管理" class="headerlink" title="IT运维面试问题总结-数据库、监控、网络管理"></a>IT运维面试问题总结-数据库、监控、网络管理</h1><h2 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h2><h3 id="简述NoSQL是什么？"><a href="#简述NoSQL是什么？" class="headerlink" title="简述NoSQL是什么？"></a>简述NoSQL是什么？</h3><p>NoSQL，指的是非关系型的数据库。NoSQL 有时也称作 Not Only SQL（意即”不仅仅是SQL”） 的缩写，其显著特点是不使用SQL作为查询语言，数据存储不需要特定的表格模式。</p>
<h3 id="简述NoSQL（非关系型）数据库和SQL（关系型）数据库的区别？"><a href="#简述NoSQL（非关系型）数据库和SQL（关系型）数据库的区别？" class="headerlink" title="简述NoSQL（非关系型）数据库和SQL（关系型）数据库的区别？"></a>简述NoSQL（非关系型）数据库和SQL（关系型）数据库的区别？</h3><p>NoSQL和SQL的主要区别有如下区别：</p>
<ul>
<li><p>存储方式：</p>
</li>
<li><ul>
<li>关系型数据库是表格式的，因此存储在表的行和列中。他们之间很容易关联协作存储，提取数据很方便。</li>
<li>NoSQL数据库则与其相反，它是大块的组合在一起。通常存储在数据集中，就像文档、键值对或者图结构。</li>
</ul>
</li>
<li><p>存储结构</p>
</li>
<li><ul>
<li>关系型数据库对应的是结构化数据，数据表都预先定义了结构（列的定义），结构描述了数据的形式和内容。预定义结构带来了可靠性和稳定性，但是修改这些数据比较困难。</li>
<li>NoSQL数据库基于动态结构，使用与非结构化数据。由于NoSQL数据库是动态结构，可以很容易适应数据类型和结构的变化。</li>
</ul>
</li>
<li><p>存储规范</p>
</li>
<li><ul>
<li>关系型数据库的数据存储为了更高的规范性，把数据分割为最小的关系表以避免重复，获得精简的空间利用。</li>
<li>NoSQL数据存储在平面数据集中，数据经常可能会重复。单个数据库很少被分隔开，而是存储成了一个整体，这样整块数据更加便于读写 。</li>
</ul>
</li>
<li><p>存储扩展</p>
</li>
<li><ul>
<li>关系型数据库数据存储在关系表中，操作的性能瓶颈可能涉及到多个表，需要通过提升计算机性能来克服，因此更多是采用纵向扩展</li>
<li>NoSQL数据库是横向扩展的，它的存储天然就是分布式的，可以通过给资源池添加更多的普通数据库服务器来分担负载。</li>
</ul>
</li>
<li><p>查询方式</p>
</li>
<li><ul>
<li>关系型数据库通过结构化查询语言来操作数据库（即通常说的SQL）。SQL支持数据库CURD操作的功能非常强大，是业界的标准用法。</li>
<li>NoSQL查询以块为单元操作数据，使用的是非结构化查询语言（UnQl），它是没有标准的。</li>
<li>关系型数据库表中主键的概念对应NoSQL中存储文档的ID。</li>
<li>关系型数据库使用预定义优化方式（比如索引）来加快查询操作，而NoSQL更简单更精确的数据访问模式。</li>
</ul>
</li>
<li><p>事务</p>
</li>
<li><ul>
<li>关系型数据库遵循ACID规则（原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)）。</li>
<li>NoSQL数据库遵循BASE原则（基本可用（Basically Availble）、软/柔性事务（Soft-state ）、最终一致性（Eventual Consistency））。</li>
<li>由于关系型数据库的数据强一致性，所以对事务的支持很好。关系型数据库支持对事务原子性细粒度控制，并且易于回滚事务。</li>
<li>NoSQL数据库是在CAP（一致性、可用性、分区容忍度）中任选两项，因为基于节点的分布式系统中，不可能同时全部满足，所以对事务的支持不是很好。</li>
</ul>
</li>
</ul>
<h3 id="简述NoSQL（非关系型）数据库和SQL（关系型）数据库的各自主要代表？"><a href="#简述NoSQL（非关系型）数据库和SQL（关系型）数据库的各自主要代表？" class="headerlink" title="简述NoSQL（非关系型）数据库和SQL（关系型）数据库的各自主要代表？"></a>简述NoSQL（非关系型）数据库和SQL（关系型）数据库的各自主要代表？</h3><p>SQL：MariaDB、MySQL、SQLite、SQLServer、Oracle、PostgreSQL。</p>
<p>NoSQL代表：Redis、MongoDB、Memcache、HBASE。</p>
<h3 id="简述MongoDB及其特点？"><a href="#简述MongoDB及其特点？" class="headerlink" title="简述MongoDB及其特点？"></a>简述MongoDB及其特点？</h3><p>MongoDB是一个开源的、基于分布式的、面向文档存储的非关系型数据库。是非关系型数据库当中功能最丰富、最像关系数据库的。其主要特点如下：</p>
<ul>
<li><code>查询丰富</code>：MongoDB最大的特点是支持的查询语言非常强大，其语法有点类似于面向对象的查询语言，几乎可以实现类似关系数据库单表查询的绝大部分功能，而且还支持对数据建立索引。</li>
<li><code>面向文档</code>：文档就是存储在MongoDB中的一条记录,是一个由键值对组成的数据结构。</li>
<li><code>模式自由</code>：MongoDB每一个Document都包含了元数据信息，每个文档之间不强迫要求使用相同的格式，同时他们也支持各种索引。</li>
<li><code>高可用性</code>：MongoDB支持在复制集(Replica Set)通过异步复制达到故障转移，自动恢复，集群中主服务器崩溃停止服务和丢失数据，备份服务器通过选举获得大多数投票成为主节点，以此来实现高可用。</li>
<li><code>水平拓展</code>：MongoDB支持分片技术，它能够支持并行处理和水平扩展。</li>
<li><code>支持丰富</code>：MongoDB另外还提供了丰富的BSON数据类型，还有MongoDB的官方不同语言的driver支持(C/C++、C#、Java、Node.js、Perl、PHP、Python、Ruby、Scala)。</li>
</ul>
<h3 id="简述MongoDB的优势有哪些？"><a href="#简述MongoDB的优势有哪些？" class="headerlink" title="简述MongoDB的优势有哪些？"></a>简述MongoDB的优势有哪些？</h3><ul>
<li>面向文档的存储：以 JSON 格式的文档保存数据。</li>
<li>任何属性都可以建立索引。</li>
<li>复制以及高可扩展性。</li>
<li>自动分片。</li>
<li>丰富的查询功能。</li>
<li>快速的即时更新。</li>
</ul>
<h3 id="简述MongoDB适应的场景和不适用的场景？"><a href="#简述MongoDB适应的场景和不适用的场景？" class="headerlink" title="简述MongoDB适应的场景和不适用的场景？"></a>简述MongoDB适应的场景和不适用的场景？</h3><p>MongoDB属于典型的非关系型数据库。</p>
<ul>
<li><p>主要适应场景</p>
</li>
<li><ul>
<li>网站实时数据：MongoDB 非常适合实时的插入，更新与查询，并具备网站实时数据存储所需的复制及高度伸缩性。</li>
<li>数据缓存：由于性能很高，MongoDB 也适合作为信息基础设施的缓存层。在系统重启之后，由 MongoDB 搭建的持久化缓存层可以避免下层的数据源过载。</li>
<li>高伸缩性场景：MongoDB 非常适合由数十或数百台服务器组成的数据库。</li>
<li>对象或 JSON 数据存储：MongoDB 的 BSON 数据格式非常适合文档化格式的存储及查询。</li>
</ul>
</li>
<li><p>不适应场景</p>
</li>
<li><ul>
<li>高度事务性系统：例如银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。</li>
<li>传统的商业智能应用：针对特定问题的 BI 数据库会对产生高度优化的查询方式。对于此类应用，数据仓库可能是更合适的选择。</li>
<li>需要复杂 SQL 查询的场景。</li>
</ul>
</li>
</ul>
<h3 id="简述MongoDB中的库、集合、文档？"><a href="#简述MongoDB中的库、集合、文档？" class="headerlink" title="简述MongoDB中的库、集合、文档？"></a>简述MongoDB中的库、集合、文档？</h3><ul>
<li><code>库</code>：MongoDB可以建立多个数据库，MongoDB默认数据库为”db”。MongoDB的单个实例可以容纳多个独立的数据库，每一个都有自己的集合和权限，不同的数据库也放置在不同的文件中。</li>
<li><code>集合</code>：MongoDB集合就是 MongoDB 文档组，类似于 RDBMS （关系数据库中的表格）。集合存在于数据库中，集合没有固定的结构。</li>
<li><code>文档</code>：MongoDB的Document是一组键值(key-value)对(即 BSON)，相当于关系型数据库的行。且不需要设置相同的字段，并且相同的字段不需要相同的数据类型。</li>
</ul>
<h3 id="简述MongoDB支持的常见数据类型？"><a href="#简述MongoDB支持的常见数据类型？" class="headerlink" title="简述MongoDB支持的常见数据类型？"></a>简述MongoDB支持的常见数据类型？</h3><p>MongoDB支持丰富的数据类型，常见的有：</p>
<ul>
<li>String：字符串。存储数据常用的数据类型。</li>
<li>Integer：整型数值。用于存储数值。</li>
<li>Boolean：布尔值。用于存储布尔值（真/假）。</li>
<li>Array：用于将数组或列表或多个值存储为一个键。</li>
<li>Date：日期时间。用 UNIX 时间格式来存储当前日期或时间。</li>
<li>Binary Data：二进制数据。用于存储二进制数据。</li>
<li>Code：代码类型。用于在文档中存储 JavaScript 代码。</li>
<li>Regular expression：正则表达式类型。用于存储正则表达式。</li>
</ul>
<h3 id="简述MongoDB索引及其作用？"><a href="#简述MongoDB索引及其作用？" class="headerlink" title="简述MongoDB索引及其作用？"></a>简述MongoDB索引及其作用？</h3><p>索引通常能够极大的提高查询的效率，如果没有索引，MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。</p>
<p>这种扫描全集合的查询效率是非常低的，特别在处理大量的数据时，查询可能要花费几十秒甚至几分钟，这对网站的性能是非常致命的。</p>
<p>索引是特殊的数据结构，索引存储在一个易于遍历读取的数据集合中，索引是对数据库表中一列或多列的值进行排序的一种结构。</p>
<h3 id="简述MongoDB常见的索引有哪些？"><a href="#简述MongoDB常见的索引有哪些？" class="headerlink" title="简述MongoDB常见的索引有哪些？"></a>简述MongoDB常见的索引有哪些？</h3><p>MongoDB常见的索引有：</p>
<ul>
<li>单字段索引（Single Field Indexes）</li>
<li>符合索引（Compound Indexes）</li>
<li>多键索引（Multikey Indexes）</li>
<li>全文索引（Text Indexes）</li>
<li>Hash索引（Hash Indexes）</li>
<li>通配符索引（Wildcard Indexes）</li>
</ul>
<h3 id="简述MongoDB复制（本）集原理？"><a href="#简述MongoDB复制（本）集原理？" class="headerlink" title="简述MongoDB复制（本）集原理？"></a>简述MongoDB复制（本）集原理？</h3><p>mongodb的复制至少需要两个节点。其中一个是主节点，负责处理客户端请求，其余的都是从节点，负责复制主节点上的数据。</p>
<p>mongodb各个节点常见的搭配方式为：一主一从、一主多从。</p>
<p>主节点记录在其上的所有操作oplog，从节点定期轮询主节点获取这些操作，然后对自己的数据副本执行这些操作，从而保证从节点的数据与主节点一致。</p>
<h3 id="简述MongoDB的复制过程？"><a href="#简述MongoDB的复制过程？" class="headerlink" title="简述MongoDB的复制过程？"></a>简述MongoDB的复制过程？</h3><p>Primary节点写入数据，Secondary通过读取Primary的oplog（即Primary的oplog.rs表）得到复制信息，开始复制数据并且将复制信息写入到自己的oplog。如果某个操作失败，则备份节点停止从当前数据源复制数据。如果某个备份节点由于某些原因挂掉了，当重新启动后，就会自动从oplog的最后一个操作开始同步。同步完成后，将信息写入自己的oplog，由于复制操作是先复制数据，复制完成后再写入oplog，有可能相同的操作会同步两份，MongoDB设定将oplog的同一个操作执行多次，与执行一次的效果是一样的。</p>
<p>当Primary节点完成数据操作后，Secondary的数据同步过程如下：</p>
<ul>
<li>1、检查自己local库的oplog.rs集合找出最近的时间戳。</li>
<li>2、检查Primary节点local库oplog.rs集合，找出大于此时间戳的记录。</li>
<li>3、将找到的记录插入到自己的oplog.rs集合中，并执行这些操作。</li>
</ul>
<h3 id="简述MongoDB副本集及其特点？"><a href="#简述MongoDB副本集及其特点？" class="headerlink" title="简述MongoDB副本集及其特点？"></a>简述MongoDB副本集及其特点？</h3><p>MongoDB副本集是一组Mongod维护相同数据集的实例，副本集可以包含多个数据承载点和多个仲裁点。在承载数据的节点中，仅有一个节点被视为主节点，其他节点称为次节点。</p>
<p>主要特点：</p>
<ul>
<li>N 个节点的集群，任何节点可作为主节点，由选举产生；</li>
<li>最小构成是：primary，secondary，arbiter，一般部署是：primary，2 secondary。</li>
<li>所有写入操作都在主节点上，同时具有自动故障转移，自动恢复；</li>
<li>成员数应该为奇数，如果为偶数的情况下添加arbiter，arbiter不保存数据，只投票。</li>
</ul>
<h3 id="简述MongoDB有哪些特殊成员？"><a href="#简述MongoDB有哪些特殊成员？" class="headerlink" title="简述MongoDB有哪些特殊成员？"></a>简述MongoDB有哪些特殊成员？</h3><p>MongoDB中Secondary角色存在一些特殊的成员类型：</p>
<ul>
<li>Priority 0（优先级0型）：不能升为主，可以用于多数据中心场景；</li>
<li>Hidden（隐藏型）：对客户端来说是不可见的，一般用作备份或统计报告用；</li>
<li>Delayed（延迟型）：数据比副集晚，一般用作 rolling backup 或历史快照。</li>
<li>Vote（投票型）：仅参与投票。</li>
</ul>
<h3 id="简述MongoDB分片集群？"><a href="#简述MongoDB分片集群？" class="headerlink" title="简述MongoDB分片集群？"></a>简述MongoDB分片集群？</h3><p>MongoDB分片集群（Sharded Cluster）：主要利用分片技术，使数据分散存储到多个分片（Shard）上，来实现高可扩展性。</p>
<p>分片是将数据水平切分到不同的物理节点。当数据量越来越大时，单台机器有可能无法存储数据或读取写入吞吐量有所降低，利用分片技术可以添加更多的机器来应对数据量增加以及读写操作的要求。</p>
<h3 id="简述MongoDB分片集群相对副本集的优势？"><a href="#简述MongoDB分片集群相对副本集的优势？" class="headerlink" title="简述MongoDB分片集群相对副本集的优势？"></a>简述MongoDB分片集群相对副本集的优势？</h3><p>MongoDB分片集群主要可以解决副本集如下的不足：</p>
<ul>
<li>副本集所有的写入操作都位于主节点；</li>
<li>延迟的敏感数据会在主节点查询；</li>
<li>单个副本集限制在12个节点；</li>
<li>当请求量巨大时会出现内存不足；</li>
<li>本地磁盘不足；</li>
<li>垂直扩展价格昂贵。</li>
</ul>
<h3 id="简述MongoDB分片集群的优势？"><a href="#简述MongoDB分片集群的优势？" class="headerlink" title="简述MongoDB分片集群的优势？"></a>简述MongoDB分片集群的优势？</h3><p>MongoDB分片集群主要有如下优势：</p>
<ul>
<li>使用分片减少了每个分片需要处理的请求数：通过水平扩展，群集可以提高自己的存储容量。比如，当插入一条数据时，应用只需要访问存储这条数据的分片。</li>
<li>使用分片减少了每个分片存储的数据：分片的优势在于提供类似线性增长的架构，提高数据可用性，提高大型数据库查询服务器的性能。当MongoDB单点数据库服务器存储成为瓶颈、单点数据库服务器的性能成为瓶颈或需要部署大型应用以充分利用内存时，可以使用分片技术。</li>
</ul>
<h3 id="简述MongoDB分片集群的架构组件？"><a href="#简述MongoDB分片集群的架构组件？" class="headerlink" title="简述MongoDB分片集群的架构组件？"></a>简述MongoDB分片集群的架构组件？</h3><p>MongoDB架构组件主要有：</p>
<ul>
<li>Shard：用于存储实际的数据块，实际生产环境中一个shard server角色可由几台机器组成一个replica set承担，防止主机单点故障。</li>
<li>Config Server：mongod实例，存储了整个 ClusterMetadata，其中包括 chunk信息。</li>
<li>Query Routers：前端路由，客户端由此接入，且让整个集群看上去像单一数据库，前端应用可以透明使用。</li>
</ul>
<h3 id="简述MongoDB分片集群和副本集群的区别？"><a href="#简述MongoDB分片集群和副本集群的区别？" class="headerlink" title="简述MongoDB分片集群和副本集群的区别？"></a>简述MongoDB分片集群和副本集群的区别？</h3><p>副本集不是为了提高读性能存在的，在进行oplog的时候，读操作是被阻塞的；</p>
<p>提高读取性能应该使用分片和索引，它的存在更多是作为数据冗余，备份。</p>
<h3 id="简述MongoDB的几种分片策略及其相互之间的差异？"><a href="#简述MongoDB的几种分片策略及其相互之间的差异？" class="headerlink" title="简述MongoDB的几种分片策略及其相互之间的差异？"></a>简述MongoDB的几种分片策略及其相互之间的差异？</h3><p>MongoDB的数据划分是基于集合级别为标准，通过shard key来划分集合数据。主要分片策略有如下三种：</p>
<ul>
<li>范围划分：通过shard key值将数据集划分到不同的范围就称为基于范围划分。对于数值型的shard key：可以虚构一条从负无穷到正无穷的直线（理解为x轴），每个shard key 值都落在这条直线的某个点上，然后MongoDB把这条线划分为许多更小的没有重复的范围成为块（chunks），一个chunk就是某些最小值到最大值的范围。</li>
<li>散列划分：MongoDB计算每个字段的hash值，然后用这些hash值建立chunks。基于散列值的数据分布有助于更均匀的数据分布，尤其是在shard key单调变化的数据集中。</li>
<li>自定义标签划分：MongoDB支持通过自定义标签标记分片的方式直接平衡数据分布策略，可以创建标签并且将它们与shard key值的范围进行关联，然后分配这些标签到各个分片上，最终平衡器转移带有标签标记的数据到对应的分片上，确保集群总是按标签描述的那样进行数据分布。标签是控制平衡器行为及集群中块分布的主要方法。</li>
</ul>
<p><code>差异</code>：</p>
<ul>
<li>基于范围划分对于范围查询比较高效。假设在shard key上进行范围查询，查询路由很容易能够知道哪些块与这个范围重叠，然后把相关查询按照这个路线发送到仅仅包含这些chunks的分片。</li>
<li>基于范围划分很容易导致数据不均匀分布，这样会削弱分片集群的功能。</li>
<li>基于散列划分是以牺牲高效范围查询为代价，它能够均匀的分布数据，散列值能够保证数据随机分布到各个分片上。</li>
</ul>
<h3 id="简述MongoDB分片集群采取什么方式确保数据分布的平衡？"><a href="#简述MongoDB分片集群采取什么方式确保数据分布的平衡？" class="headerlink" title="简述MongoDB分片集群采取什么方式确保数据分布的平衡？"></a>简述MongoDB分片集群采取什么方式确保数据分布的平衡？</h3><p>新加入的数据及服务器都会导致集群数据分布不平衡，MongoDB采用两种方式确保数据分布的平衡：</p>
<ul>
<li><p>拆分</p>
<p>拆分是一个后台进程，防止块变得太大。当一个块增长到指定块大小的时候，拆分进程就会块一分为二，整个拆分过程是高效的。不会涉及到数据的迁移等操作。</p>
</li>
<li><p>平衡</p>
<p>平衡器是一个后台进程，管理块的迁移。平衡器能够运行在集群任何的mongd实例上。当集群中数据分布不均匀时，平衡器就会将某个分片中比较多的块迁移到拥有块较少的分片中，直到数据分片平衡为止。</p>
<p>分片采用后台操作的方式管理着源分片和目标分片之间块的迁移。在迁移的过程中，源分片中的块会将所有文档发送到目标分片中，然后目标分片会获取并应用这些变化。最后，更新配置服务器上关于块位置元数据。</p>
</li>
</ul>
<h3 id="简述MongoDB备份及恢复方式？"><a href="#简述MongoDB备份及恢复方式？" class="headerlink" title="简述MongoDB备份及恢复方式？"></a>简述MongoDB备份及恢复方式？</h3><p>mongodb备份恢复方式通常有以下三种：</p>
<ul>
<li><code>文件快照方式</code>：此方式相对简单，需要系统文件支持快照和mongod必须启用journal。可以在任何时刻创建快照。恢复时，确保没有运行mongod，执行快照恢复操作命令，然后启动mongod进程，mongod将重放journal日志。</li>
<li><code>复制数据文件方式</code>：直接拷贝数据目录下的一切文件，但是在拷贝过程中必须阻止数据文件发生更改。因此需要对数据库加锁，以防止数据写入。恢复时，确保mongod没有运行，清空数据目录，将备份的数据拷贝到数据目录下，然后启动mongod。</li>
<li><code>使用mongodump和mongorestore方式</code>：在Mongodb中我们使用mongodump命令来备份MongoDB数据。该命令可以导出所有数据到指定目录中。恢复时，使用mongorestore命令来恢复MongoDB数据。该命令可以从指定目录恢复相应数据。</li>
</ul>
<h3 id="简述MongoDB的聚合操作？"><a href="#简述MongoDB的聚合操作？" class="headerlink" title="简述MongoDB的聚合操作？"></a>简述MongoDB的聚合操作？</h3><p>聚合操作能够处理数据记录并返回计算结果。聚合操作能将多个文档中的值组合起来，对成组数据执行各种操作，返回单一的结果。它相当于 SQL 中的 count(*) 组合 group by。对于 MongoDB 中的聚合操作，应该使用aggregate()方法。</p>
<h3 id="简述MongoDB中的GridFS机制？"><a href="#简述MongoDB中的GridFS机制？" class="headerlink" title="简述MongoDB中的GridFS机制？"></a>简述MongoDB中的GridFS机制？</h3><p>GridFS是一种将大型文件存储在MongoDB中的文件规范。使用GridFS可以将大文件分隔成多个小文档存放，这样我们能够有效的保存大文档，而且解决了BSON对象有限制的问题。</p>
<h3 id="简述MongoDB针对查询优化的措施？"><a href="#简述MongoDB针对查询优化的措施？" class="headerlink" title="简述MongoDB针对查询优化的措施？"></a>简述MongoDB针对查询优化的措施？</h3><p>MongoDB查询优化大致可能从如下步骤着手：</p>
<ul>
<li><p>第一步：找出慢速查询</p>
<p>如下方式开启内置的查询分析器,记录读写操作效率：</p>
<p>db.setProfilingLevel(n,{m}),n的取值可选0,1,2；</p>
<p>查询监控结果：监控结果保存在一个特殊的集合system.profile里。</p>
</li>
<li><ul>
<li>0：默认值，表示不记录；</li>
<li>1：表示记录慢速操作，如果值为1，m必须赋值单位为ms，用于定义慢速查询时间的阈值；</li>
<li>2：表示记录所有的读写操作。</li>
</ul>
</li>
<li><p>第二步：分析慢速查询</p>
<p>找出慢速查询的原因，通常可能的原因有：应用程序设计不合理、不正确的数据模型、硬件配置问题、缺少索引等</p>
</li>
<li><p>第三部：根据不同的分析结果进行优化，如建立索引。</p>
</li>
</ul>
<h3 id="简述MongoDB的更新操作是否会立刻fsync到磁盘？"><a href="#简述MongoDB的更新操作是否会立刻fsync到磁盘？" class="headerlink" title="简述MongoDB的更新操作是否会立刻fsync到磁盘？"></a>简述MongoDB的更新操作是否会立刻fsync到磁盘？</h3><p>不会，磁盘写操作默认是延时执行的，写操作可能在两三秒（默认在60秒内）后到达磁盘，可通过syncPeriodSecs参数进行配置。</p>
<h3 id="简述MySQL索引及其作用？"><a href="#简述MySQL索引及其作用？" class="headerlink" title="简述MySQL索引及其作用？"></a>简述MySQL索引及其作用？</h3><p>是数据库管理系统中一个排序的数据结构，根据不同的存储引擎索引分为Hash索引、B+树索引等。常见的InnoDB存储引擎的默认索引实现为：B+树索引。</p>
<p>索引可以协助快速查询、更新数据库表中数据。</p>
<h3 id="简述MySQL中什么是事务？"><a href="#简述MySQL中什么是事务？" class="headerlink" title="简述MySQL中什么是事务？"></a>简述MySQL中什么是事务？</h3><p>事务是一系列的操作，需要要符合ACID特性，即：事务中的操作要么全部成功，要么全部失败。</p>
<h3 id="简述MySQL事务之间的隔离？"><a href="#简述MySQL事务之间的隔离？" class="headerlink" title="简述MySQL事务之间的隔离？"></a>简述MySQL事务之间的隔离？</h3><p>MySQL事务支持如下四种隔离：</p>
<ul>
<li><code>未提交读</code>(Read Uncommitted)：允许脏读，其他事务只要修改了数据，即使未提交，本事务也能看到修改后的数据值。也就是可能读取到其他会话中未提交事务修改的数据。</li>
<li><code>提交读</code>(Read Committed)：只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)。</li>
<li><code>可重复读</code>(Repeated Read)：可重复读。无论其他事务是否修改并提交了数据，在这个事务中看到的数据值始终不受其他事务影响。</li>
<li><code>串行读</code>(Serializable)：完全串行化的读，每次读都需要获得表级共享锁，读写相互都会阻塞。</li>
</ul>
<h3 id="简述MySQL锁及其作用？"><a href="#简述MySQL锁及其作用？" class="headerlink" title="简述MySQL锁及其作用？"></a>简述MySQL锁及其作用？</h3><p>锁机制是为了避免，在数据库有并发事务的时候，可能会产生数据的不一致而诞生的的一个机制。锁从类别上分为：</p>
<ul>
<li><code>共享锁</code>：又叫做读锁，当用户要进行数据的读取时，对数据加上共享锁，共享锁可以同时加上多个。</li>
<li><code>排他锁</code>：又叫做写锁，当用户要进行数据的写入时，对数据加上排他锁，排他锁只可以加一个，他和其他的排他锁,共享锁都相斥。</li>
</ul>
<h3 id="简述MySQL表中为什么建议添加主键？"><a href="#简述MySQL表中为什么建议添加主键？" class="headerlink" title="简述MySQL表中为什么建议添加主键？"></a>简述MySQL表中为什么建议添加主键？</h3><p>主键是数据库确保数据行在整张表唯一性的保障，即使数据库中表没有主键，也建议添加一个自增长的ID列作为主键，设定了主键之后，在后续的删改查的时候可能更加快速以及确保操作数据范围安全。</p>
<h3 id="简述MySQL所支持的存储引擎？"><a href="#简述MySQL所支持的存储引擎？" class="headerlink" title="简述MySQL所支持的存储引擎？"></a>简述MySQL所支持的存储引擎？</h3><p>MySQL支持多种存储引擎，常见的有InnoDB、MyISAM、Memory、Archive等。通常使用InnoDB引擎都是最合适的，InnoDB也是MySQL的默认存储引擎。</p>
<h3 id="简述MySQL-InnoDB引擎和MyISAM引擎的差异？"><a href="#简述MySQL-InnoDB引擎和MyISAM引擎的差异？" class="headerlink" title="简述MySQL InnoDB引擎和MyISAM引擎的差异？"></a>简述MySQL InnoDB引擎和MyISAM引擎的差异？</h3><ul>
<li>InnoDB支持事物，而MyISAM不支持事物。</li>
<li>InnoDB支持行级锁，而MyISAM支持表级锁。</li>
<li>InnoDB支持MVCC, 而MyISAM不支持。</li>
<li>InnoDB支持外键，而MyISAM不支持。</li>
<li>InnoDB不支持全文索引，而MyISAM支持。</li>
</ul>
<h3 id="简述MySQL主从复制过程？"><a href="#简述MySQL主从复制过程？" class="headerlink" title="简述MySQL主从复制过程？"></a>简述MySQL主从复制过程？</h3><ul>
<li>1、Slave上面的IO线程连接上Master，并请求从指定日志文件的指定位置（或者从最开始的日志）之后的日志内容；</li>
<li>2、Master接收到来自Slave的IO线程的请求后，通过负责复制的IO线程根据请求信息读取指定日志指定位置之后的日志信息，返回给Slave端的IO线程。返回信息中除了日志所包含的信息之外，还包括本次返回的信息在Master端binary log文件的名称以及在Binary log中的位置；</li>
<li>3、Slave的IO线程收到信息后，将接收到的日志内容依次写入到Slave端的RelayLog文件（mysql-relay-lin.xxxxx）的最末端，并将读取到的Master端的bin-log的文件名和位置记录到master-info文件中，以便在下一次读取的时候能够明确知道从什么位置开始读取日志；</li>
<li>4、Slave的SQL线程检测到Relay Log中新增加了内容后，会马上解析该Log文件中的内容成为在Master端真实执行时候的那些可执行的查询或操作语句，并在自身执行那些查询或操作语句，这样，实际上就是在master端和Slave端执行了同样的查询或操作语句，所以两端的数据是完全一样的。</li>
</ul>
<h3 id="简述MySQL常见的读写分离方案？"><a href="#简述MySQL常见的读写分离方案？" class="headerlink" title="简述MySQL常见的读写分离方案？"></a>简述MySQL常见的读写分离方案？</h3><p>MySQL+Amoeba读写分离方案：Amoeba(变形虫)项目，这个工具致力于MySQL的分布式数据库前端代理层，它主要在应用层访问MySQL的时候充当SQL路由功能。具有负载均衡、高可用性、SQL 过滤、读写分离、可路由相关的到目标数据库、可并发请求多台数据库合并结果。通过Amoeba你能够完成多数据源的高可用、负载均衡、数据切片、读写分离的功能。</p>
<p>MySQL+MMM读写分离方案：MMM即Multi-Master Replication Manager for MySQL，mysql多主复制管理器是关于mysql主主复制配置的监控、故障转移和管理的一套可伸缩的脚本套件(在任何时候只有一个节点可以被写入)。MMM也能对从服务器进行读负载均衡，通过MMM方案能实现服务器的故障转移，从而实现mysql的高可用。MMM不仅能提供浮动IP的功能，如果当前的主服务器挂掉后，会将你后端的从服务器自动转向新的主服务器进行同步复制，不用手工更改同步配置。</p>
<h3 id="简述MySQL常见的高可用方案？"><a href="#简述MySQL常见的高可用方案？" class="headerlink" title="简述MySQL常见的高可用方案？"></a>简述MySQL常见的高可用方案？</h3><ul>
<li><code>MySQL主从复制</code>：Mysql内建的复制功能是构建大型，高性能应用程序的基础。将Mysql的数据分布在多个节点（slaves）之上，复制过程中一个服务器充当主服务器，而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件，并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。</li>
<li><code>MySQL双主</code>：参考MySQL主从复制。</li>
<li><code>MySQL双主多从</code>：参考MySQL主从复制。</li>
<li><code>MySQL复制+Keepalived高可用</code>：MySQL自身的复制，对外基于Keepalived技术，暴露一个VIP，从而实现高可用。</li>
<li><code>Heartbeat + MySQL 实现MySQL的高可用</code>：通过Heartbeat的心跳检测和资源接管、集群中服务的监测、失效切换等功能，结合MySQL来实现高可用性。</li>
</ul>
<h3 id="简述MySQL常见的优化措施？"><a href="#简述MySQL常见的优化措施？" class="headerlink" title="简述MySQL常见的优化措施？"></a>简述MySQL常见的优化措施？</h3><p>MySQL可通过如下方式优化：</p>
<ul>
<li>1、开启查询缓存，优化查询。</li>
<li>2、使用explain判断select查询，从而分析查询语句或是表结构的性能瓶颈，然后有针对性的进行优化。</li>
<li>3、为搜索字段建索引</li>
<li>4、对于有限定范围取值的字段，推荐使用 ENUM 而不是 VARCHAR。</li>
<li>5、垂直分表。</li>
<li>6、选择正确的存储引擎。</li>
</ul>
<h3 id="简述MySQL常见备份方式和工具？"><a href="#简述MySQL常见备份方式和工具？" class="headerlink" title="简述MySQL常见备份方式和工具？"></a>简述MySQL常见备份方式和工具？</h3><ul>
<li><p>MySQL自带</p>
<p>mysqldump：mysqldump支持基于innodb的热备份，使用mysqldump完全备份+二进制日志可以实现基于时间点的恢复，通常适合备份数据比较小的场景 。</p>
</li>
<li><p>系统层面</p>
<p>tar备份：可以使用tar之类的系统命令对整个数据库目录进行打包备份。</p>
<p>lvm快照备份：可基于文件系统的LVM制作快照，进行对整个数据库目录所在的逻辑卷备份。</p>
</li>
<li><p>第三方备份工具</p>
<p>可使用其他第三方工具进行备份，如xtrabackup工具，该工具支持innodb的物理热备份，支持完全备份、增量备份，而且速度非常快，支持innodb存储引起的数据在不同数据库之间迁移，支持复制模式下的从机备份恢复备份恢复。</p>
</li>
</ul>
<h2 id="监控"><a href="#监控" class="headerlink" title="监控"></a>监控</h2><h3 id="简述常见的监控软件？"><a href="#简述常见的监控软件？" class="headerlink" title="简述常见的监控软件？"></a>简述常见的监控软件？</h3><p>常见的监控软件有：</p>
<ul>
<li><code>Cacti</code>：是一套基于PHP、MySQL、SNMP及RRDTool开发的网络流量监测图形分析工具。</li>
<li><code>Zabbix</code>：Zabbix是一个企业级的高度集成开源监控软件，提供分布式监控解决方案。可以用来监控设备、服务等可用性和性能。</li>
<li><code>Open-falcon</code>：open-falcon是一款用golang和python写的监控系统，由小米启动这个项目。</li>
<li><code>Prometheus</code>：Prometheus是由SoundCloud开发的开源监控报警系统和时序列数据库(TSDB)。Prometheus使用Go语言开发，是Google BorgMon监控系统的开源版本。</li>
</ul>
<h3 id="简述Prometheus及其主要特性？"><a href="#简述Prometheus及其主要特性？" class="headerlink" title="简述Prometheus及其主要特性？"></a>简述Prometheus及其主要特性？</h3><p>Prometheus是一个已加入CNCF的开源监控报警系统和时序列数据库项目，通过不同的组件完成数据的采集，数据的存储和告警。</p>
<p>Prometheus主要特性：</p>
<ul>
<li><p>多维数据模型</p>
</li>
<li><ul>
<li>时间序列数据通过 metric 名和键值对来区分。</li>
<li>所有的 metrics 都可以设置任意的多维标签。</li>
<li>数据模型更随意，不需要刻意设置为以点分隔的字符串。</li>
<li>可以对数据模型进行聚合，切割和切片操作。</li>
<li>支持双精度浮点类型，标签可以设为全 unicode。</li>
</ul>
</li>
<li><p>灵活的查询语句（PromQL），可以利用多维数据完成复杂的查询</p>
</li>
<li><p>Prometheus server 是一个单独的二进制文件，不依赖（任何分布式）存储，支持 local 和 remote 不同模型</p>
</li>
<li><p>采用 http 协议，使用 pull 模式，拉取数据，或者通过中间网关推送方式采集数据</p>
</li>
<li><p>监控目标，可以采用服务发现或静态配置的方式</p>
</li>
<li><p>支持多种统计数据模型，图形化友好</p>
</li>
<li><p>高效：一个 Prometheus server 可以处理数百万的 metrics</p>
</li>
<li><p>适用于以机器为中心的监控以及高度动态面向服务架构的监控</p>
</li>
</ul>
<h3 id="简述Prometheus主要组件及其功能？"><a href="#简述Prometheus主要组件及其功能？" class="headerlink" title="简述Prometheus主要组件及其功能？"></a>简述Prometheus主要组件及其功能？</h3><p>Prometheus 的主要模块包含：prometheus server, exporters, push gateway, PromQL, Alertmanager, WebUI 等。</p>
<ul>
<li>1、<code>prometheus server</code>：定期从静态配置的 targets 或者服务发现（主要是DNS、consul、k8s、mesos等）的 targets 拉取数据，用于收集和存储时间序列数据。</li>
<li>2、<code>exporters</code>：负责向prometheus server做数据汇报，暴露一个http服务的接口给Prometheus server定时抓取。而不同的数据汇报由不同的exporters实现，比如监控主机有node-exporters，mysql有MySQL server exporter。</li>
<li>3、<code>push gateway</code>：主要使用场景为，当Prometheus 采用 pull 模式，可能由于不在一个子网或者防火墙原因，导致 Prometheus 无法直接拉取各个 target 数据。此时需要push gateway接入，以便于在监控业务数据的时候，将不同数据汇总, 由 Prometheus 统一收集。实现机制类似于zabbix-proxy功能。</li>
<li>4、<code>Alertmanager</code>：从 Prometheus server 端接收到 alerts 后，会进行去除重复数据，分组，并路由到对收的接受方式，发出报警，即主要实现prometheus的告警功能。AlertManager的整体工作流程如下图所示:</li>
<li>5、<code>webui</code>：Prometheus内置一个简单的Web控制台，可以查询指标，查看配置信息或者Service Discovery等，实践中通常结合Grafana，Prometheus仅作为Grafana的数据源。</li>
</ul>
<h3 id="简述Prometheus的机制？"><a href="#简述Prometheus的机制？" class="headerlink" title="简述Prometheus的机制？"></a>简述Prometheus的机制？</h3><p>Prometheus简单机制如下：</p>
<ul>
<li>Prometheus以其Server为核心，用于收集和存储时间序列数据。Prometheus Server 从监控目标中拉取数据，或通过push gateway间接的把监控目标的监控数据存储到本地HDD/SSD中。</li>
<li>用户接口界面通过各种UI使用PromQL查询语言从Server获取数据。</li>
<li>一旦Server检测到异常，会推送告警到AlertManager，由告警管理负责去通知相关方。</li>
</ul>
<h3 id="简述Prometheus中什么是时序数据？"><a href="#简述Prometheus中什么是时序数据？" class="headerlink" title="简述Prometheus中什么是时序数据？"></a>简述Prometheus中什么是时序数据？</h3><p>Prometheus 存储的是时序数据,，时序数据是指按照相同时序(相同的名字和标签)，以时间维度存储连续的数据的集合。时序(time series) 是由名字(Metric)，以及一组 key/value 标签定义的，具有相同的名字以及标签属于相同时序。</p>
<h3 id="简述Prometheus时序数据有哪些类型？"><a href="#简述Prometheus时序数据有哪些类型？" class="headerlink" title="简述Prometheus时序数据有哪些类型？"></a>简述Prometheus时序数据有哪些类型？</h3><p>Prometheus 时序数据分为 Counter, Gauge, Histogram, Summary 四种类型。</p>
<ul>
<li><code>Counter</code>：计数器表示收集的数据是按照某个趋势（增加／减少）一直变化的，通常用它记录服务请求总量，错误总数等。</li>
<li><code>Gauge</code>：计量器表示搜集的数据是一个瞬时的，与时间没有关系，可以任意变高变低，往往可以用来记录内存使用率、磁盘使用率等。</li>
<li><code>Histogram</code>：直方图 Histogram 主要用于对一段时间范围内的数据进行采样，（通常是请求持续时间或响应大小），并能够对其指定区间以及总数进行统计，通常我们用它计算分位数的直方图。</li>
<li><code>Summary</code>：汇总Summary 和 直方图Histogram 类似，主要用于表示一段时间内数据采样结果，（通常是请求持续时间或响应大小），它直接存储了 quantile 数据，而不是根据统计区间计算出来的。</li>
</ul>
<h3 id="简述Zabbix及其优势？"><a href="#简述Zabbix及其优势？" class="headerlink" title="简述Zabbix及其优势？"></a>简述Zabbix及其优势？</h3><p>Zabbix是一个企业级的高度集成开源监控软件，提供分布式监控解决方案。可以用来监控设备、服务等可用性和性能。其主要优势有：</p>
<ul>
<li>自由开放源代码产品，可以对其进行任意修改和二次开发，采用GPL协议；</li>
<li>安装和配置简单；</li>
<li>搭建环境简单，基于开源软件构建平台；</li>
<li>完全支持Linux、Unix、Windows、AIX、BSD等平台，采用C语言编码，系统占用小，数据采集性能和速度非常快；</li>
<li>数据采集持久存储到数据库，便于对监控数据的二次分析；</li>
<li>非常丰富的扩展能力，轻松实现自定义监控项和实现数据采集。</li>
</ul>
<h3 id="简述Zabbix体系架构？"><a href="#简述Zabbix体系架构？" class="headerlink" title="简述Zabbix体系架构？"></a>简述Zabbix体系架构？</h3><p>Zabbix体系相对清晰，其主要组件有：</p>
<ul>
<li><code>Zabbix Server</code>：负责接收agent发送的报告信息的核心组件，所有配置、统计数据及操作数据均由其组织进行。</li>
<li><code>Database Storage</code>：专用于存储所有配置信息，以及有zabbix收集的数据。</li>
<li><code>Web interface</code>（frontend）：zabbix的GUI接口，通常与server运行在同一台机器上。</li>
<li><code>Proxy</code>：可选组件，常用于分布式监控环境中，代理Server收集部分被监控数据并统一发往Server端。</li>
<li><code>Agent</code>：部署在被监控主机上，负责收集本地数据并发往Server端或者Proxy端。</li>
</ul>
<h3 id="简述Zabbix所支持的监控方式？"><a href="#简述Zabbix所支持的监控方式？" class="headerlink" title="简述Zabbix所支持的监控方式？"></a>简述Zabbix所支持的监控方式？</h3><p>目前由zabbix提供包括但不限于以下事项类型的支持：</p>
<ul>
<li><code>Zabbix agent checks</code>：这些客户端来进行数据采集，又分为Zabbix agent（被动模式：客户端等着服务器端来要数据），Zabbix agent (active)（主动模式：客户端主动发送数据到服务器端）</li>
<li><code>SNMP agent checks</code>：SNMP方式，如果要监控打印机网络设备等支持SNMP设备的话，但是又不能安装agent的设备。</li>
<li><code>SNMP traps</code> ：</li>
<li><code>IPMI checks</code>：IPMI即智能平台管理接口，现在是业界通过的标准。用户可以利用IPMI监视服务器的物理特性，如温度、电压、电扇工作状态、电源供应以及机箱入侵等。</li>
</ul>
<h4 id="简述Zabbix分布式及其适应场景？"><a href="#简述Zabbix分布式及其适应场景？" class="headerlink" title="简述Zabbix分布式及其适应场景？"></a>简述Zabbix分布式及其适应场景？</h4><p>zabbix proxy 可以代替 zabbix server 收集性能和可用性数据,然后把数据汇报给 zabbix server，并且在一定程度上分担了zabbix server 的压力。</p>
<p>此外，当所有agents和proxy报告给一个Zabbix server并且所有数据都集中收集时，使用proxy是实现集中式和分布式监控的最简单方法。</p>
<p>zabbix proxy 使用场景:</p>
<ul>
<li>监控远程区域设备</li>
<li>监控本地网络不稳定区域</li>
<li>当 zabbix 监控上千设备时,使用它来减轻 server 的压力</li>
<li>简化分布式监控的维护</li>
</ul>
<h2 id="网络管理"><a href="#网络管理" class="headerlink" title="网络管理"></a>网络管理</h2><h3 id="简述什么是CDN？"><a href="#简述什么是CDN？" class="headerlink" title="简述什么是CDN？"></a>简述什么是CDN？</h3><p>CDN即内容分发网络，是在现有网络中增加一层新的网络架构，从而实现将源站内容发布和传送到最靠近用户的边缘地区，使用户可以就近访问想要的内容，提高用户访问的响应速度。</p>
<blockquote>
<ul>
<li>作者：木二</li>
<li>链接：<a target="_blank" rel="noopener" href="https://www.yuque.com/docs/share/d3dd1e8e-6828-4da7-9e30-6a4f45c6fa8e">https://www.yuque.com/docs/share/d3dd1e8e-6828-4da7-9e30-6a4f45c6fa8e</a></li>
</ul>
</blockquote>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/interview/" rel="tag">interview</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-interview/IT运维面试问题总结-运维工具、开源应用(Ceph、Docker、Apache、Nginx等)"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/interview/IT%E8%BF%90%E7%BB%B4%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93-%E8%BF%90%E7%BB%B4%E5%B7%A5%E5%85%B7%E3%80%81%E5%BC%80%E6%BA%90%E5%BA%94%E7%94%A8(Ceph%E3%80%81Docker%E3%80%81Apache%E3%80%81Nginx%E7%AD%89)/"
    >IT运维面试问题总结-运维工具、开源应用(Ceph、Docker、Apache、Nginx等).md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/interview/IT%E8%BF%90%E7%BB%B4%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93-%E8%BF%90%E7%BB%B4%E5%B7%A5%E5%85%B7%E3%80%81%E5%BC%80%E6%BA%90%E5%BA%94%E7%94%A8(Ceph%E3%80%81Docker%E3%80%81Apache%E3%80%81Nginx%E7%AD%89)/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/interview/">interview</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="IT运维面试问题总结-运维工具、开源应用-Ceph、Docker、Apache、Nginx等"><a href="#IT运维面试问题总结-运维工具、开源应用-Ceph、Docker、Apache、Nginx等" class="headerlink" title="IT运维面试问题总结-运维工具、开源应用(Ceph、Docker、Apache、Nginx等)"></a>IT运维面试问题总结-运维工具、开源应用(Ceph、Docker、Apache、Nginx等)</h1><h2 id="运维工具"><a href="#运维工具" class="headerlink" title="运维工具"></a>运维工具</h2><h3 id="简述Ansible及其优势？"><a href="#简述Ansible及其优势？" class="headerlink" title="简述Ansible及其优势？"></a>简述Ansible及其优势？</h3><p><code>Ansible</code>是一款极其简单的开源的自动化运维工具，基于Python开发，集合了众多运维工具(puppet, cfengine, chef, func, fabric)的优点。实现了批量系统配置，批量程序部署，批量运行命令等功能。同时Ansible是基于模块工作，其实现批量部署的是ansible所运行的模块。</p>
<p>Ansible其他重要的优势：</p>
<ul>
<li>跨平台支持：Ansible在物理、虚拟、云和容器环境中为Linux、Windows、UNIX和网络设备提供无代理支持。</li>
<li>人类可读的自动化：Ansible playbook以YAML文本文件的形式编写，易于阅读，有助于确保每个人都理解他们将要做的事情。</li>
<li>对应用程序的完美描述：Ansible playbook可以进行任何更改，并且可以描述和记录应用程序环境的每个细节。</li>
<li>易于管理的版本控制：Ansible剧本和项目是纯文本。它们可以像源代码一样处理，并放在现有的版本控制系统中。</li>
<li>支持动态库存：Ansible管理的机器列表可以从外部资源动态更新，以便随时捕获所有受管服务器的正确的当前列表，无论基础设施或位置如何。</li>
<li>易于与其他系统集成的编排：HP SA、Puppet、Jenkins、Red Hat Satellite，以及存在于环境中的其他系统，都可以被利用并集成到Ansible工作流中。</li>
</ul>
<h3 id="简述Ansible工作机制及其特性？"><a href="#简述Ansible工作机制及其特性？" class="headerlink" title="简述Ansible工作机制及其特性？"></a>简述Ansible工作机制及其特性？</h3><p>Ansible是一款自动化运维工具，基于Python开发，具有批量系统配置, 批量程序部署, 批量运行命令等功能。</p>
<p>其工作机制如下：</p>
<ul>
<li>1、用户使用Ansible或Playbook，在服务器中断输入Ansible的Ad-Hoc命令集或Playbook；</li>
<li>2、Ansible遵循预先编排的规则将Playbooks逐条拆解为Play；</li>
<li>3、Play组织成Ansible可识别的任务（Task）；</li>
<li>4、Task会调用任务所涉及的所有模块（Module）和插件（Plugin）；</li>
<li>5、读取Inventroy中定义的主机列表；</li>
<li>6、通过SSH认证（默认）将任务集以临时文件或命令的形式传输到远程客户端执行并返回执行结果。其特性如下：</li>
<li>7、no agents：不需要在被管控主机上安装任何客户端，只需SSH、Python即可，建议Python版本为2.6.6以上；</li>
<li>8、no server：无服务器端, 使用时直接运行命令即可；</li>
<li>9、modules in any languages：基于模块工作, 丰富的内置模块，可使用任意语言开发模块；</li>
<li>10、yaml, not code：使用yaml语言定制剧本playbook，易于管理，API简单明了；</li>
<li>11、ssh by default：基于SSH工作，整个过程简单、方便、安全，建议使用公钥方式认证；</li>
<li>12、strong multi-tier solution：可实现多级指挥。</li>
</ul>
<h3 id="简述Ansible中如何保存敏感数据？"><a href="#简述Ansible中如何保存敏感数据？" class="headerlink" title="简述Ansible中如何保存敏感数据？"></a>简述Ansible中如何保存敏感数据？</h3><p>在ansible内容中保留秘密数据并仍然公开共享，那么可以在playbooks中使用Vault。Ansible Vault，它包含在Ansible中，可以加密和解密Ansible使用的任何结构化数据文件。</p>
<h3 id="简述Ansible适合的场景？"><a href="#简述Ansible适合的场景？" class="headerlink" title="简述Ansible适合的场景？"></a>简述Ansible适合的场景？</h3><p>Ansible将编排与配置管理、供应和应用程序部署结合并统一在一个易于使用的平台上。Ansible的一些主要场景包括:</p>
<ul>
<li>配置管理：集中配置文件管理和部署是Ansible的一个常见场景。</li>
<li>应用程序部署：当使用Ansible定义应用程序，并使用Ansible Tower管理部署时，团队可以有效地管理从开发到生产的整个应用程序生命周期。</li>
<li>部署：当在系统上部署或安装应用程序时，Ansible和Ansible Tower可以帮助简化供应系统的流程，无论是PXE启动的裸金属服务器或虚拟机，还是从模板创建虚拟机或云实例。</li>
<li>持续交付：创建CI/CD管道需要许多团队的协调和参与。如果没有一个简单的自动化平台，团队协作很难完成。而Ansible playbook在应用程序的整个生命周期中可以保持适当的部署(和管理)</li>
<li>安全性和审计：当安全策略在Ansible中定义时，可以将站点范围的安全策略的扫描和修复集成到其他自动化流程中。安全性是部署的所有内容中不可或缺的一部分。</li>
<li>编排：配置本身不能定义环境，需要定义多个配置如何交互，并确保可以将不同的部分作为一个整体来管理。</li>
</ul>
<h3 id="简述Ansible-Inventory？"><a href="#简述Ansible-Inventory？" class="headerlink" title="简述Ansible Inventory？"></a>简述Ansible Inventory？</h3><p>Ansible中受管主机列在主机清单（inventory）文本文件中，清单还将这些系统组织成group，以便更容易地进行批量管理。一个Inventory定义了Ansible将管理的主机集合。这些主机还可以分配至组，可以对组进行批量管理。组可以包含子组，主机可以是多个组的成员。Inventory根据类型可分为静态清单和动态清单：</p>
<ul>
<li>静态主机Inventory可以由文本文件定义。</li>
<li>动态主机Inventory可以由脚本或其他程序根据需要使用外部信息提供者生成。</li>
</ul>
<h3 id="简述Ansible配置文件优先级？"><a href="#简述Ansible配置文件优先级？" class="headerlink" title="简述Ansible配置文件优先级？"></a>简述Ansible配置文件优先级？</h3><p>Ansible 只使用最高优先级配置文件中的设置，其它配置文件中的设置将被忽略。即使存在其他优先级较低的文件，它们的设置也将被忽略，并且不会与所选配置文件中的设置相结合。</p>
<p>$ANSIBLE_CONFIG环境变量指定的任何文件都将覆盖所有其他配置文件。如果没有设置该变量，接下来将检查运行ansible命令的目录以查找ansible.cfg文件。如果该文件不存在，则检查用户的主目录以查找.ansible.cfg文件。如上配置文件都不存在时，才使用全局/etc/ansible/ansible.cfg文件。</p>
<h3 id="简述Ansible-ad-hoc命令？"><a href="#简述Ansible-ad-hoc命令？" class="headerlink" title="简述Ansible ad-hoc命令？"></a>简述Ansible ad-hoc命令？</h3><p>Ad-Hoc命令是一种快速执行单个Ansible任务的方法，适合于不需要永久保存该任务，临时执行的场景。Ad-Hoc是简单的控制台操作，无需编写剧本就可以运行。它们对于快速测试和更改非常有用。</p>
<h3 id="简述Ansible-ad-hoc和playbook的区别？"><a href="#简述Ansible-ad-hoc和playbook的区别？" class="headerlink" title="简述Ansible ad-hoc和playbook的区别？"></a>简述Ansible ad-hoc和playbook的区别？</h3><ul>
<li>Ad-Hoc 命令可以作为一次性命令对一组目标主机运行单个、简单的任务。</li>
<li>Ad-Hoc 不适合复杂配置管理或编配场景，Ad-Hoc 一次只能调用一个模块和一组参数。当需要多个操作时，必须使用多个 Ad-Hoc 来执行。</li>
<li>playbook可以实现以一种简易重复的方式对一组目标主机运行多个复杂的任务。</li>
<li>Playbook 是描述要在受管主机上实施的必要配置或程序性步骤的文件。</li>
<li>Playbook 为配置管理和部署提供了强大而灵活的解决方案。</li>
<li>Playbook 可以将冗长而复杂的管理任务转变为可轻松重复的历程，并且可预测成果然而。</li>
<li>playbook 是一个文本文件，其中包含一个或多个按顺序运行的play的列表。</li>
<li>playbook中，可以将playbook中的tasks保存为人类可读且可立即运行的形式。</li>
<li>play 是一组有序的任务，应该对从目录中选择的主机运行。</li>
</ul>
<h3 id="简述Ansible变量？"><a href="#简述Ansible变量？" class="headerlink" title="简述Ansible变量？"></a>简述Ansible变量？</h3><p>Ansible 利用变量存储整个 Ansible 项目文件中可重复使用的值，从而可以简化项目的创建和维护，并减少错误的发生率。在定义Ansible变量时，通常有如下三种范围的变量：</p>
<ul>
<li>global范围：从命令行或Ansible配置中设置的变量；</li>
<li>play范围：在 play 和相关结构中设置的变量；</li>
<li>host范围：inventory、facts 或 register 的变量，在主机组和个别主机上设置的变量。</li>
</ul>
<h3 id="简述Ansible如何实现任务的循环？"><a href="#简述Ansible如何实现任务的循环？" class="headerlink" title="简述Ansible如何实现任务的循环？"></a>简述Ansible如何实现任务的循环？</h3><p>简单循环：</p>
<ul>
<li>Ansible支持使用loop在一组item上迭代任务；</li>
<li>loop可以使用列表中的每个项、列表中每个文件的内容、生成的数字序列或使用更复杂的结构来重复任务。</li>
<li>使用loop使管理员不必编写使用相同模块的多个任务。</li>
</ul>
<p>复杂（嵌套）循环：</p>
<ul>
<li>with_nested键用于嵌套循环，循环在循环中运行。它需要一个包含两个或多个列表的列表。例如，将一个列表划分为两个列表，任务将迭代第一个列表中的每一项与第二个列表中的每一项。</li>
</ul>
<h3 id="简述Ansible-hanlder？"><a href="#简述Ansible-hanlder？" class="headerlink" title="简述Ansible hanlder？"></a>简述Ansible hanlder？</h3><p>Ansible模块被设计成幂等的，即在一个适当编写的剧本中，剧本及其任务可以在不更改受管主机的情况下多次运行，除非它们需要进行更改以使受管主机达到所需的状态。</p>
<p>然而，有时当一个任务对系统进行了更改后同时需要运行另一个任务。例如，对服务的配置文件的更改可能需要重新加载服务，以便更改后的配置生效。此时就需要使用hanlder程序。handler程序是响应由其他任务组成的通知的任务。每个handler程序都有一个全局惟一的名称，并在剧本中任务块的末尾触发。</p>
<p>如果没有任务通过名称调用handler程序，它将不会运行。</p>
<p>如果一个或多个任务都调用handler程序，它将在剧中的所有其他任务完成后仅运行一次。</p>
<p>因为handler程序是任务，所以可以在handler程序中使用与处理任何其他任务相同的模块。通常，handler程序用于重新启动主机和重新启动服务。</p>
<p>handler程序可以视为非活动任务，只有在使用notify语句显式调用时才会触发这些任务。</p>
<h3 id="简述Ansible-Block？"><a href="#简述Ansible-Block？" class="headerlink" title="简述Ansible Block？"></a>简述Ansible Block？</h3><p>在 playbook 中， blocks 是囊括了任务的子句；</p>
<p>blocks 允许对任务进行逻辑分组，并可用于控制任务的执行方式，例如，管理员可以定义一组主要任务和一组附加任务，附加任务仅在第一组失败时执行。为此，可利用三个关键字在 playbook 中使用块：</p>
<ul>
<li>block：定义要运行的主要任务；</li>
<li>rescue：定义将在 block 子句中定义的任务失败时运行的任务；</li>
<li>always：定义始终都独立运行的任务，不论 block 和 rescue 子句中定义的任务是成功还是失败。</li>
</ul>
<h3 id="简述Ansible如何处理play错误的？"><a href="#简述Ansible如何处理play错误的？" class="headerlink" title="简述Ansible如何处理play错误的？"></a>简述Ansible如何处理play错误的？</h3><p>Ansible审查每个任务的返回代码，以确定任务是否成功或失败。默认情况下，当一个任务失败时，Ansible会立即中止该主机上的其他操作，并跳过所有后续任务。</p>
<p>实际生产中，若希望即使任务失败也能继续执行play，Ansible也包含了多种特性用于管理任务错误：</p>
<p>忽略任务失败：在任务中使用ignore_errors关键字忽略错误，即使任务失败，也继续在主机上执行playbook。</p>
<h3 id="简述Ansible角色？"><a href="#简述Ansible角色？" class="headerlink" title="简述Ansible角色？"></a>简述Ansible角色？</h3><p>数据中心有各种不同类型的主机。如web服务器、数据库服务器，基于开发环境的服务器。随着时间的推移，具有处理所有这些情况的任务和人员的Ansible playbook将变得庞大而复杂。</p>
<ul>
<li>角色允许将复杂的剧本组织成独立的、更小的剧本和文件。</li>
<li>角色提供了一种从外部文件加载任务、处理程序和变量的方法。</li>
<li>角色也可关联和引用静态的文件和模板。</li>
<li>角色可以编写成满足普通用途需求，并且能被重复利用。</li>
<li>定义角色的文件具有特定的名称，并以严格的目录结构进行组织。</li>
</ul>
<h3 id="简述Ansible-Galaxy？"><a href="#简述Ansible-Galaxy？" class="headerlink" title="简述Ansible Galaxy？"></a>简述Ansible Galaxy？</h3><p>Ansible Galaxy是一个由各种Ansible管理员和用户编写的Ansible角色的公共库。它是一个包含数千个Ansible角色的归档文件，并且有一个可搜索的数据库，帮助Ansible用户识别可能帮助他们完成管理任务的角色。Ansible Galaxy包括指向新用户和角色开发人员的文档和视频的链接。</p>
<h3 id="简述Ansible如何控制任务的并行执行？"><a href="#简述Ansible如何控制任务的并行执行？" class="headerlink" title="简述Ansible如何控制任务的并行执行？"></a>简述Ansible如何控制任务的并行执行？</h3><p>通过在所有主机上并行运行任务，Ansible可以对剧本的执行进行更多的控制。默认情况下，Ansible默认最多并行5个，因此它将同时在5台不同的机器上运行一个特定的任务。Ansible可以通过配置forks来设置并行执行任务数量。</p>
<p>同时Ansible也可以通过serial来减少ork数量所指定的并行书，serial关键字主要用于控制滚动更新，避免一次性更新过多的节点。</p>
<h3 id="简述Ansible故障后的排查思路？"><a href="#简述Ansible故障后的排查思路？" class="headerlink" title="简述Ansible故障后的排查思路？"></a>简述Ansible故障后的排查思路？</h3><ul>
<li><code>日志判断</code>：默认情况下，Ansible没有配置为将其输出，记录到任何日志文件中。可通过ansible.cfg配置文件default部分中的log_path参数或$ANSIBLE_LOG环境变量进行配置。然后通过日志进行定位。</li>
<li><code>Debug模块</code>：调试模块是Ansible可用的模块之一，它可以更好地了解控制节点上正在进行的操作。这个模块可以在playbook执行时为某个变量提供值。</li>
<li><code>syntax-check</code>：通过ansible-playbook 命令的 –syntax-check命选项检查剧本的YAML语法。</li>
<li><code>diff</code>：Ansible还提供了–diff选项。此选项报告对受管主机上的模板文件所做的更改。如果与–check选项一起使用，这些更改将显示出来，而不是实际执行。从而判断Ansible整个过程需要做何种更改。</li>
</ul>
<h2 id="开源应用"><a href="#开源应用" class="headerlink" title="开源应用"></a>开源应用</h2><h3 id="简述Ceph的优势及其特点？"><a href="#简述Ceph的优势及其特点？" class="headerlink" title="简述Ceph的优势及其特点？"></a>简述Ceph的优势及其特点？</h3><p>Ceph是一个分布式的数据对象存储，Ceph相对其他存储系统具有如下优势：</p>
<ul>
<li>CRUSH算法：ceph摒弃了传统的集中式存储元数据寻址的方案，而使用CRUSH算法完成数据的寻址操作。能够实现各类负载的副本放置规则，例如跨机房、机架感知等。Crush算法有相当强大的扩展性，理论上支持数千个存储节点，从而增强了Ceph弹性扩展和高可用性。</li>
<li>高可用：通过CRUSH算法指定副本的物理存储位置以分隔故障域，支持数据强一致性，ceph可以忍受多种故障场景并自动尝试并行修复。</li>
<li>高扩展性：Ceph本身并没有主控节点，扩展起来比较容易，并且理论上，它的性能会随着磁盘数量的增加而线性增长。</li>
<li>特性丰富：Ceph支持三种调用接口：对象存储，块存储，文件系统挂载。三种方式可以一同使用。Ceph主要特点如下：</li>
<li>统一存储；</li>
<li>无任何单点故障；</li>
<li>数据多份冗余；</li>
<li>存储容量可扩展；</li>
<li>自动容错及故障自愈。</li>
</ul>
<h3 id="简述Ceph存储体系架构？"><a href="#简述Ceph存储体系架构？" class="headerlink" title="简述Ceph存储体系架构？"></a>简述Ceph存储体系架构？</h3><p>Ceph体系架构主要由RADOS和RADOS GW和RBD以及CephFS构成。</p>
<ul>
<li><p><code>RADOS</code>（Reliable, Autonomic Distributed Object Store）是Ceph的底层核心，RADOS本身也是分布式存储系统，CEPH所有的存储功能都是基于RADOS实现。RADOS由两个组件组成：OSD和Monitor。</p>
</li>
<li><ul>
<li>OSD主要提供存储资源，每一个disk、SSD、RAID group或者一个分区都可以成为一个OSD，而每个OSD还将负责向该对象的复杂节点分发和恢复；</li>
<li>Monitor维护Ceph集群并监控Ceph集群的全局状态，提供一致性的决策。</li>
</ul>
</li>
<li><p><code>RADOS GW</code>和<code>RBD</code>：RADOS GateWay、RBD其作用是在librados库的基础上提供抽象层次更高、更便于应用或客户端使用的上层接口。其中，RADOS GW是一个提供与Amazon S3和Swift兼容的RESTful API的gateway，以供相应的对象存储应用开发使用。RBD则提供了一个标准的块设备接口，常用于在虚拟化的场景下为虚拟机创建volume。</p>
</li>
<li><p><code>CEPHFS</code>：CEPHFS则提供了POSIX接口，用户可直接通过客户端挂载使用。</p>
</li>
</ul>
<h3 id="简述Ceph-Pool有几种类型？"><a href="#简述Ceph-Pool有几种类型？" class="headerlink" title="简述Ceph Pool有几种类型？"></a>简述Ceph Pool有几种类型？</h3><p>Ceph存储池Pool是Ceph存储集群用于存储对象的逻辑分区。池类型确定池用于确保数据持久性的保护机制，Ceph有两种Pool类型：</p>
<p>replication类型：在集群中分布每个对象的多个副本。</p>
<p>erasure coding类型：将每个对象分割成块，并将它们与其他擦除编码块一起分发，以使用自动纠错机制保护对象。</p>
<h3 id="简述Ceph-Pool、PG、ODDs的关系？"><a href="#简述Ceph-Pool、PG、ODDs的关系？" class="headerlink" title="简述Ceph Pool、PG、ODDs的关系？"></a>简述Ceph Pool、PG、ODDs的关系？</h3><p>Ceph存储池Pool是Ceph存储集群用于存储对象的逻辑分区。</p>
<p>Pool中存在一定的数量的PG，PG将对象存储在一组由CRUSH算法确定的osd中。</p>
<p>Ceph使用CRUSH算法将对象分配给池中的一个PG，根据池的配置和CRUSH算法，PG自动映射到一组OSDs。</p>
<p>一个PG里包含一堆对象，一个对象只能属于一个PG。</p>
<h3 id="简述Ceph节点的角色？"><a href="#简述Ceph节点的角色？" class="headerlink" title="简述Ceph节点的角色？"></a>简述Ceph节点的角色？</h3><p>所有Ceph存储集群的部署都始于部署一个个Ceph节点、网络和Ceph存储集群。Ceph存储集群至少需要一个Ceph Monitor和两个OSD守护进程。而运行Ceph文件系统客户端时，则必须要有元数据服务器（Metadata Server）。</p>
<ul>
<li>Ceph OSDs：Ceph OSD守护进程（ Ceph OSD ）的功能是存储数据，处理数据的复制、恢复、回填、再均衡，并通过检查其他OSD守护进程的心跳来向Ceph Monitors提供一些监控信息。当Ceph存储集群设定为有2个副本时，至少需要2个OSD守护进程，集群才能达到active+clean状态（Ceph默认有3个副本）。</li>
<li>Monitors：Ceph Monitor维护着展示集群状态的各种图表，包括监视器图、OSD图、归置组（PG）图、和CRUSH 图。</li>
<li>MDSs：Ceph元数据服务器（MDS）为Ceph文件系统存储元数据（也就是说，Ceph块设备和Ceph 对象存储不使用MDS）。元数据服务器使得POSIX文件系统的客户端，可以在不对Ceph存储集群造成负担的前提下，执行诸如ls、find等基本命令。</li>
</ul>
<h3 id="简述Ceph的适应场景？"><a href="#简述Ceph的适应场景？" class="headerlink" title="简述Ceph的适应场景？"></a>简述Ceph的适应场景？</h3><p>Ceph的应用场景主要由它的架构确定，Ceph提供对象存储、块存储和文件存储。</p>
<ul>
<li>LIBRADOS应用</li>
</ul>
<p>Librados提供了应用程序对RADOS的直接访问，目前Librados已经提供了对C、C++、Java、Python、Ruby和PHP的支持。</p>
<ul>
<li>RADOSGW应用</li>
</ul>
<p>此类场景基于Librados之上，增加了HTTP协议，提供RESTful接口并且兼容S3、Swfit接口。RADOSGW将Ceph集群作为分布式对象存储，对外提供服务。</p>
<ul>
<li>RBD应用</li>
</ul>
<p>此类场景也是基于Librados之上的，细分为下面两种应用场景。</p>
<p>第一种应用场景为虚拟机提供块设备。通过Librbd可以创建一个块设备（Container），然后通过QEMU/KVM附加到VM上。通过Container和VM的解耦，使得块设备可以被绑定到不同的VM上。</p>
<p>第二种应用场景为主机提供块设备。这种场景是传统意义上的理解的块存储。</p>
<p>以上两种方式都是将一个虚拟的块设备分片存储在RADOS中，都会利用数据条带化提高数据并行传输，都支持块设备的快照、COW（Copy-On-Write）克隆。最重要的是RBD还支持Live migration。</p>
<ul>
<li>CephFS（Ceph文件系统）</li>
</ul>
<p>此类应用是基于RADOS实现的PB级分布式文件系统，其中引入MDS（Meta Date Server），它主要为兼容POSIX文件系统提供元数据，比如文件目录和文件元数据。</p>
<h3 id="简述Docker的特性？"><a href="#简述Docker的特性？" class="headerlink" title="简述Docker的特性？"></a>简述Docker的特性？</h3><p>Docker主要有如下特性：</p>
<ul>
<li><p>标准化</p>
</li>
<li><ul>
<li>保证一致的运行环境</li>
<li>弹性伸缩，快速扩容</li>
<li>方便迁移</li>
<li>持续集成、持续交付与持续部署</li>
</ul>
</li>
<li><p>高性能</p>
</li>
<li><ul>
<li>不需要进行硬件虚拟以及运行完整的操作系统</li>
</ul>
</li>
<li><p>轻量级</p>
</li>
<li><ul>
<li>快速启动</li>
<li>隔离性</li>
<li>进程隔离</li>
</ul>
</li>
</ul>
<h3 id="简述Docker容器的几种状态？"><a href="#简述Docker容器的几种状态？" class="headerlink" title="简述Docker容器的几种状态？"></a>简述Docker容器的几种状态？</h3><p>Docker容器可以有四种状态：</p>
<ul>
<li>运行</li>
<li>已暂停</li>
<li>重新启动</li>
<li>已退出</li>
</ul>
<h3 id="简述Dockerfile、Docker镜像和Docker容器的区别？"><a href="#简述Dockerfile、Docker镜像和Docker容器的区别？" class="headerlink" title="简述Dockerfile、Docker镜像和Docker容器的区别？"></a>简述Dockerfile、Docker镜像和Docker容器的区别？</h3><p><code>Dockerfile</code> 是软件的<code>原材料</code>，<code>Docker镜像</code> 是<code>软件的交付品</code>，而 <code>Docker容器</code> 则可以认为是<code>软件的运行态</code>。从应用软件的角度来看，Dockerfile、Docker 镜像与 Docker 容器分别代表软件的三个不同阶段，Dockerfile 面向开发，Docker 镜像成为交付标准，Docker 容器则涉及部署与运维。</p>
<h3 id="简述Docker与KVM（虚拟机）的区别？"><a href="#简述Docker与KVM（虚拟机）的区别？" class="headerlink" title="简述Docker与KVM（虚拟机）的区别？"></a>简述Docker与KVM（虚拟机）的区别？</h3><ul>
<li>容器部署简单，虚拟机部署相对复杂。</li>
</ul>
<p>虚拟化技术依赖物理CPU和内存，是硬件级别的；</p>
<p>而docker构建在操作系统上，利用操作系统的containerization技术，所以docker甚至可以在虚拟机上运行。</p>
<ul>
<li>容器秒级启动，虚拟机通常分钟级启动。</li>
</ul>
<p>传统的虚拟化技术在构建系统的时候较为复杂，需要大量的人力；</p>
<p>而docker可以通过Dockfile来构建整个容器，重启和构建速度很快。</p>
<ul>
<li>容器需要的资源（如磁盘、CPU、内存）相对更少。</li>
<li>容器比较轻便，虚拟机相对较重。</li>
</ul>
<p>虚拟化系统一般都是指操作系统级概念，比较复杂，称为“系统”；</p>
<p>而docker开源而且轻量，称为“容器”，单个容器适合部署少量应用，比如部署一个redis、一个memcached。</p>
<h3 id="简述Docker主要使用的技术？"><a href="#简述Docker主要使用的技术？" class="headerlink" title="简述Docker主要使用的技术？"></a>简述Docker主要使用的技术？</h3><p>容器主要使用如下技术：</p>
<ul>
<li>Cgroup：资源控制</li>
<li>Namespace：访问隔离</li>
<li>rootfs：文件系统隔离</li>
<li>容器引擎（用户态工具）：生命周期控制</li>
</ul>
<h3 id="简述Docker体系架构？"><a href="#简述Docker体系架构？" class="headerlink" title="简述Docker体系架构？"></a>简述Docker体系架构？</h3><p>Docker体系相对简单，主要涉及如下5个组件：</p>
<ul>
<li>Docker客户端 – Docker</li>
</ul>
<p>docker客户端则扮演着docker服务端的远程控制器，可以用来控制docker的服务端进程。</p>
<ul>
<li>Docker服务端 – Docker Daemon</li>
</ul>
<p>docker服务端是一个服务进程，管理着所有的容器。</p>
<ul>
<li>Docker镜像 – Image</li>
</ul>
<p>docker镜像，一个能够运行在docker容器上的一组程序文件，是一个只读的模板，不包含任何动态数据。</p>
<ul>
<li>Docker容器 – Docker Container</li>
</ul>
<p>docker容器，就是运行程序的载体，容器是镜像运行时的实体。</p>
<ul>
<li>Docker镜像仓库 – Registry</li>
</ul>
<p>Docker仓库是集中存放镜像文件的场所，Docker仓库分为公开仓库（Public）和私有仓库（Private）两种形式。</p>
<h3 id="简述Docker如何实现网络隔离？"><a href="#简述Docker如何实现网络隔离？" class="headerlink" title="简述Docker如何实现网络隔离？"></a>简述Docker如何实现网络隔离？</h3><p>Docker利用了网络的命名空间特性，实现了不同容器之间的网络隔离。命名空间可以支持网络协议栈的多个实例，独立的协议栈被隔离到不同的命名空间中。</p>
<p>因此处于不同命名空间中的网络栈是完全隔离的，彼此之间无法通信。每个独立的命名空间中可以有自己独立的路由表及独立的iptables设置来提供包转发、NAT及IP包过滤等功能。</p>
<h3 id="简述Linux文件系统和Docker文件系统？"><a href="#简述Linux文件系统和Docker文件系统？" class="headerlink" title="简述Linux文件系统和Docker文件系统？"></a>简述Linux文件系统和Docker文件系统？</h3><p><code>Linux文件系统</code>：由bootfs和rootfs组成，bootfs主要包含bootloader和kernel，bootloader主要是引导加载kernel，当kernel被加载到内存之后bootfs就被卸载掉了。rootfs包含的就是典型linux系统中/dev,/proc,/bin,/etc等标准目录。</p>
<p><code>Docker文件系统</code>：Docker容器是建立在Aufs分层文件系统基础上的，Aufs支持将不同的目录挂载到同一个虚拟文件系统下，并实现一种layer的概念。Aufs将挂载到同一虚拟文件系统下的多个目录分别设置成read-only，read-write以及whiteout-able权限。docker 镜像中每一层文件系统都是read-only。</p>
<h3 id="简述Docker网络模式？"><a href="#简述Docker网络模式？" class="headerlink" title="简述Docker网络模式？"></a>简述Docker网络模式？</h3><p>Docker使用Linux的Namespaces技术来进行资源隔离，其中Network Namespace实现隔离网络。</p>
<p>一个Network Namespace提供了一份独立隔离的网络环境，包括网卡、路由、Iptable规则等，Docker网络有如下四种模式：</p>
<ul>
<li><code>host模式</code>：host模式下容器将不会获得独立的Network Namespace，该模式下与宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡，不会配置独有的IP等，而是使用宿主机的IP和端口。</li>
<li><code>container模式</code>：Container 网络模式是 Docker 中一种较为特别的网络的模式，处于container模式下的 Docker 容器会共享其他容器的网络环境，因此，两个或以上的容器之间不存在网络隔离，而配置container模式的容器又与宿主机以及除此之外其他的容器存在网络隔离。</li>
<li><code>none模式</code>：none模式下，Docker容器拥有自己的Network Namespace，但是，并不为Docker容器进行任何网络配置和构造任何网络环境。Docker 容器采用了none 网络模式，那么容器内部就只能使用loopback网络设备，不会再有其他的网络资源。Docker Container的none网络模式意味着不给该容器创建任何网络环境，容器只能使用127.0.0.1的本机网络。</li>
<li><code>bridge模式</code>：bridge模式是Docker默认的网络设置，此模式会为每一个容器分配Network Namespace、设置IP等，并将该宿主机上的Docker容器连接到一个虚拟网桥上。</li>
</ul>
<h3 id="简述Docker跨主机通信的网络实现方式？"><a href="#简述Docker跨主机通信的网络实现方式？" class="headerlink" title="简述Docker跨主机通信的网络实现方式？"></a>简述Docker跨主机通信的网络实现方式？</h3><p>docker跨主机通信按原理可通过以下三种方式实现：</p>
<ul>
<li>直接路由方式：直接在不同宿主机之间添加静态路由；</li>
<li>桥接方式（如pipework）：通过静态指定容器IP为宿主机IP同一个网络的形式，即可实现。</li>
<li>Overlay隧道方式：使用overlay网络实现，Overlay网络指在现有网络层之上叠加的虚拟化技术，实现应用在网络上的承载，并能与其他网络业务分离，并且以基于IP的网络技术为主，如flannel、ovs+gre。</li>
</ul>
<h3 id="简述flannel网络模型实现原理？"><a href="#简述flannel网络模型实现原理？" class="headerlink" title="简述flannel网络模型实现原理？"></a>简述flannel网络模型实现原理？</h3><p>Flannel为每个host分配一个subnet，容器从subnet中分配IP，这些IP可以在host间路由，容器间无需使用nat和端口映射即可实现跨主机通信。每个subnet都是从一个更大的IP池中划分的，flannel会在每个主机上运flanneld的agent，负责从池子中分配subnet。</p>
<p>Flannel使用etcd存放网络配置、已分配的subnet、host的IP等信息，Flannel数据包在主机间转发是由backend实现的，目前已经支持UDP、VxLAN、host-gw、AWS VPC和GCE路由等多种backend。</p>
<h3 id="简述什么是Apache服务器？"><a href="#简述什么是Apache服务器？" class="headerlink" title="简述什么是Apache服务器？"></a>简述什么是Apache服务器？</h3><p>Apache服务器是一个非常流行、功能强大并且开源的Web服务器，基于HTTP超文本传输协议运行，这一协议提供了服务器和客户端Web浏览器通信的标准。它支持SSL、CGI文件、虚拟主机等许多功能特性。</p>
<h3 id="简述Apache虚拟主机？"><a href="#简述Apache虚拟主机？" class="headerlink" title="简述Apache虚拟主机？"></a>简述Apache虚拟主机？</h3><p>Apache虚拟主机相当于一个在同一台服务器中却相互独立的站点，从而实现一台主机对外提供多个 web 服务，每个虚拟主机之间是独立的，互不影响的。Apache具有两种类型的虚拟主机：基于名称的虚拟主机和基于IP的虚拟主机。</p>
<h3 id="简述Apache的Worker-MPM和Prefork-MPM之间的区别？"><a href="#简述Apache的Worker-MPM和Prefork-MPM之间的区别？" class="headerlink" title="简述Apache的Worker MPM和Prefork MPM之间的区别？"></a>简述Apache的Worker MPM和Prefork MPM之间的区别？</h3><p>它们都是MPM，Worker和Prefork有它们各自在Apache上的运行机制，取决于哪种模式启动Apache。Worker MPM和Prefork MPM基本的区别在于它们产生子进程的处理过程。</p>
<ul>
<li>1、Prefork MPM中，一个主httpd进行被启动，这个主进程会管理所有其它子进程为客户端请求提供服务。Worker MPM中一个httpd进程被激活，则会使用不同的线程来为客户端请求提供服务.</li>
<li>2、Prefork MPM使用多个子进程，每一个进程带有一个线程，Worker MPM使用多个子进程，每一个进程带有多个线程。</li>
<li>3、Prefork MPM中的连接处理，每一个进程一次处理一个连接而在Worker MPM中每一个线程一次处理一个连接。</li>
<li>4、内存占用Prefork MPM占用庞大的内存，而Worker MPM占用更小的内存。</li>
</ul>
<h3 id="简述Nginx是什么及其主要特点？"><a href="#简述Nginx是什么及其主要特点？" class="headerlink" title="简述Nginx是什么及其主要特点？"></a>简述Nginx是什么及其主要特点？</h3><p>Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器。可以作为一个HTTP服务器进行网站的发布处理，同时也可以作为反向代理进行负载均衡的实现。其主要特点有：</p>
<ul>
<li>占有内存少，并发能力强。</li>
<li>Nginx使用基于事件驱动架构，使得其可以支持数以百万级别的TCP连接。</li>
<li>高度的模块化和自由软件许可证使得第三方模块非常丰富。</li>
<li>Nginx是一个跨平台服务器，可以运行在Linux，Windows，FreeBSD，Solaris，AIX，Mac OS等操作系统上。</li>
</ul>
<h3 id="简述Nginx和Apache的差异？"><a href="#简述Nginx和Apache的差异？" class="headerlink" title="简述Nginx和Apache的差异？"></a>简述Nginx和Apache的差异？</h3><ul>
<li>Nginx是一个基于事件的Web服务器，Apache是一个基于流程的服务器；</li>
<li>Nginx所有请求都由一个线程处理，Apache单个线程处理单个请求；</li>
<li>Nginx避免子进程的概念，Apache是基于子进程的；</li>
<li>Nginx在内存消耗和连接方面更好，Apache在内存消耗和连接方面一般；</li>
<li>Nginx的性能和可伸缩性不依赖于硬件，Apache依赖于CPU和内存等硬件；</li>
<li>Nginx支持热部署，Apache不支持热部署；</li>
<li>Nginx对于静态文件处理具有更高效率，Apache相对一般；</li>
<li>Nginx在反向代理场景具有明显优势，Apache相对一般。</li>
</ul>
<h3 id="简述Nginx主要应用的场景？"><a href="#简述Nginx主要应用的场景？" class="headerlink" title="简述Nginx主要应用的场景？"></a>简述Nginx主要应用的场景？</h3><p>基于Nginx的特性，Nginx的应用场景主要有：</p>
<ul>
<li>http服务器：Nginx是一个http服务可以独立提供http服务，可以做网页静态服务器。</li>
<li>虚拟主机：可以实现在一台服务器虚拟出多个网站。</li>
<li>正反代理：负载均衡或加速，当网站的访问量达到一定程度后，单台服务器不能满足用户的请求时，需要用多台服务器集群可以使用Nginx做反向代理，并且多台服务器可以平均分担负载。</li>
</ul>
<h3 id="简述Nginx-HTTP连接和请求的关系？"><a href="#简述Nginx-HTTP连接和请求的关系？" class="headerlink" title="简述Nginx HTTP连接和请求的关系？"></a>简述Nginx HTTP连接和请求的关系？</h3><p>HTTP是建立在TCP上，一次HTTP请求需要先建立TCP三次握手（称为TCP连接），在连接的基础上再进行HTTP请求。</p>
<p>HTTP请求建立在一次TCP连接基础上，对于HTTP会话，一次TCP连接可以建立多次HTTP请求。</p>
<h3 id="简述Nginx支持哪些访问控制方式？"><a href="#简述Nginx支持哪些访问控制方式？" class="headerlink" title="简述Nginx支持哪些访问控制方式？"></a>简述Nginx支持哪些访问控制方式？</h3><ul>
<li>连接限制：Nginx自带的limit_conn_module模块（TCP连接频率限制模块）和limit_req_mudule模块（HTTP请求频率限制模块）支持对连接频率以及请求频率、来源进行限制，通常可可以用来防止DDOS攻击。</li>
<li>IP限制：Nginx使用http_access_module模块可实现基于IP的访问控制，但通过代理可以绕过限制。</li>
<li>账号限制：Nginx使用http_auth_basic_module模块可实现用户密码的登录限制。</li>
<li>流量限制：Nginx使用http_core_moduleblock模块可实现客户端传送响应的速率限制。</li>
</ul>
<h3 id="简述Nginx-Master进程和Worker节点？"><a href="#简述Nginx-Master进程和Worker节点？" class="headerlink" title="简述Nginx Master进程和Worker节点？"></a>简述Nginx Master进程和Worker节点？</h3><p><code>master进程</code>主要用来管理worker进程，包含：接收来自外界的信号，向各worker进程发送信号，监控worker进程的运行状态，当worker进程退出后(异常情况下)，会自动重新启动新的worker进程。</p>
<p><code>worker进程</code>则是处理基本的网络事件。多个worker进程之间是对等的，他们同等竞争来自客户端的请求，各进程互相之间是独立的。一个请求，只可能在一个worker进程中处理，一个worker进程，不可能处理其它进程的请求。</p>
<h3 id="简述Nginx如何处理HTTP请求？"><a href="#简述Nginx如何处理HTTP请求？" class="headerlink" title="简述Nginx如何处理HTTP请求？"></a>简述Nginx如何处理HTTP请求？</h3><ul>
<li>首先，Nginx 在启动时，会解析配置文件，获取需要监听的端口与 IP 地址，然后在 Nginx 的 Master 进程里面先初始化好这个监控的Socket（创建 Socket，设置 addr、绑定ip和端口，然后listen 监听)。</li>
<li>然后，再 fork 出多个子进程出来。</li>
<li>之后，子进程会竞争 accept 新的连接。此时，客户端就可以向 nginx 发起连接了。当客户端与nginx完成TCP三次握手，与 nginx 建立好一个连接后。此时，某一个子进程会 accept 成功，得到这个建立好的连接的 Socket ，然后创建 nginx 对连接的封装。</li>
<li>接着，设置读写事件处理函数，并添加读写事件来与客户端进行数据的交换。</li>
<li>最后，Nginx 或客户端来主动关掉连接，完成整个HTTP请求的处理。</li>
</ul>
<h3 id="简述Nginx对于HTTP请求采用哪两种机制进行处理？"><a href="#简述Nginx对于HTTP请求采用哪两种机制进行处理？" class="headerlink" title="简述Nginx对于HTTP请求采用哪两种机制进行处理？"></a>简述Nginx对于HTTP请求采用哪两种机制进行处理？</h3><p>Nginx 是一个高性能的 Web 服务器，能够同时处理大量的并发请求。它结合多进程机制和异步非阻塞机制 。</p>
<ul>
<li><p>多进程机制：服务器每当收到一个客户端请求时，就有服务器主进程 （master process）生成一个子进程（worker process）和客户端建立连接进行交互，直到连接断开，该子进程就结束了。</p>
</li>
<li><ul>
<li>使用进程的好处是各个进程之间相互独立，不需要加锁，减少了使用锁对性能造成影响。</li>
<li>其次，采用独立的进程，可以让进程互相之间不会影响 ，如果一个进程发生异常退出时，其它进程正常工作，master进程则很快启动新的worker进程，确保服务不会中断，从而将风险降到最低。</li>
<li>缺点是操作系统生成一个子进程需要进行 内存复制等操作，在资源和时间上会产生一定的开销。当有大量请求时，会导致系统性能下降 。</li>
</ul>
</li>
<li><p>异步非阻塞机制：每个工作进程使用异步非阻塞方式，可以处理多个客户端请求 。</p>
</li>
<li><ul>
<li>当某个工作进程接收到客户端请求以后，调用 IO 进行处理，如果不能立即得到结果，就去处理其他请求（即为非阻塞 ）。而客户端在此期间也无需等待响应，可以进行其他任务（即为 异步 ）。</li>
<li>当IO返回时，就会通知此工作进程。该进程得到通知，暂时挂起当前处理的事务去响应客户端请求。</li>
</ul>
</li>
</ul>
<h3 id="简述Nginx支持哪些类型的虚拟主机？"><a href="#简述Nginx支持哪些类型的虚拟主机？" class="headerlink" title="简述Nginx支持哪些类型的虚拟主机？"></a>简述Nginx支持哪些类型的虚拟主机？</h3><p>对于Nginx而言，每一个虚拟主机相当于一个在同一台服务器中却相互独立的站点，从而实现一台主机对外提供多个 web 服务，每个虚拟主机之间是独立的，互不影响的。通过 Nginx 可以实现虚拟主机的配置，Nginx 支持三种类型的虚拟主机配置：</p>
<ul>
<li>基于 IP 的虚拟主机（较少使用）</li>
<li>基于域名的虚拟主机</li>
<li>基于端口的虚拟主机</li>
</ul>
<h3 id="简述Nginx缓存及其作用？"><a href="#简述Nginx缓存及其作用？" class="headerlink" title="简述Nginx缓存及其作用？"></a>简述Nginx缓存及其作用？</h3><p>缓存对于Web至关重要，尤其对于大型高负载Web站点。Nginx缓存可作为性能优化的一个重要手段，可以极大减轻后端服务器的负载。通常对于静态资源，即较少经常更新的资源，如图片，css或js等进行缓存，从而在每次刷新浏览器的时候，不用重新请求，而是从缓存里面读取，这样就可以减轻服务器的压力。</p>
<h3 id="简述Nginx作为代理缓存后，客户端访问的过程？"><a href="#简述Nginx作为代理缓存后，客户端访问的过程？" class="headerlink" title="简述Nginx作为代理缓存后，客户端访问的过程？"></a>简述Nginx作为代理缓存后，客户端访问的过程？</h3><p>使用Nginx作为代理缓存后，可加快客户端的访问，其过程大致如下：</p>
<ul>
<li>1、第一步：客户端第一次向Nginx请求数据A；</li>
<li>2、第二步：当Nginx发现缓存中没有数据A时，会向服务端请求数据A；</li>
<li>3、第三步：服务端接收到Nginx发来的请求，则返回数据A到Nginx，并且缓存在Nginx；</li>
<li>4、第四步：Nginx返回数据A给客户端应用；</li>
<li>5、第五步：客户端第二次向Nginx请求数据A；</li>
<li>6、第六步：当Nginx发现缓存中存在数据A时，则不会请求服务端；</li>
<li>7、第七步：Nginx把缓存中的数据A返回给客户端应用。</li>
</ul>
<h3 id="简述Nginx代理及其类型？"><a href="#简述Nginx代理及其类型？" class="headerlink" title="简述Nginx代理及其类型？"></a>简述Nginx代理及其类型？</h3><p>代理（forward）是一个位于客户端和原始服务器(origin server)之间的服务器，即代理服务器。为了从原始服务器取得内容，客户端向代理服务器发送一个请求并指定目标原始服务器，然后代理服务器向原始服务器转交请求并将获得的内容返回给客户端。其通常有如下三种代理模式：</p>
<ul>
<li>正向代理（forward proxy）：一个位于客户端和原始服务器(origin server)之间的服务器，为了从原始服务器取得内容，客户端向代理发送一个请求并指定目标(原始服务器)，然后代理向原始服务器转交请求并将获得的内容返回给客户端。</li>
<li>反向代理（reverse proxy）：指以代理服务器来接受 Internet上的连接请求，然后将请求，发给内部网络上的服务器并将从服务器上得到的结果返回给 Internet 上请求连接的客户端，此时代理服务器对外就表现为一个反向代理服务器。</li>
<li>透明代理</li>
</ul>
<h3 id="简述Nginx盗链及如何防护？"><a href="#简述Nginx盗链及如何防护？" class="headerlink" title="简述Nginx盗链及如何防护？"></a>简述Nginx盗链及如何防护？</h3><p><code>盗链</code> 指的是在自己的界面展示非本服务器上的内容，通过技术手段获得其他服务器的资源。绕过他人资源展示页面，在自己页面向用户提供此内容，从而减轻自己服务器的负担，因为真实的空间和流量来自其他服务器。</p>
<p>因此，通常为了避免被盗链，通常Web服务器建议配置防盗链。Nginx防盗链其主要防盗链思路是能区别哪些请求是非正常用户请求，对于非正常用户的请求直接反馈403或重定向至其他页面。</p>
<h3 id="简述Nginx负载均衡的意义？"><a href="#简述Nginx负载均衡的意义？" class="headerlink" title="简述Nginx负载均衡的意义？"></a>简述Nginx负载均衡的意义？</h3><p>负载均衡是将负载分摊到多个操作单元上执行，从而提高服务的可用性和响应速度，带给用户更好的体验。对于Web应用，通过负载均衡，可以将一台服务器的工作扩展到多台服务器中执行，提高整个网站的负载能力。其本质采用一个调度者，保证所有后端服务器都将性能充分发挥，从而保持服务器集群的整体性能最优，这就是负载均衡。</p>
<h3 id="简述Nginx负载均衡的优势？"><a href="#简述Nginx负载均衡的优势？" class="headerlink" title="简述Nginx负载均衡的优势？"></a>简述Nginx负载均衡的优势？</h3><p>Nginx作为负载均衡器具有极大的优势，主要体现在：</p>
<ul>
<li>高并发连接</li>
<li>内存消耗少</li>
<li>配置文件非常简单</li>
<li>成本低廉</li>
<li>支持Rewrite重写规则</li>
<li>内置的健康检查功能</li>
<li>节省带宽</li>
<li>稳定性高</li>
</ul>
<h3 id="简述Nginx负载均衡主要的均衡机制（策略）？"><a href="#简述Nginx负载均衡主要的均衡机制（策略）？" class="headerlink" title="简述Nginx负载均衡主要的均衡机制（策略）？"></a>简述Nginx负载均衡主要的均衡机制（策略）？</h3><p>Nginx作为负载均衡器具有极大的优势，其负载均衡策略可以划分为两大类：内置策略和扩展策略，扩展策略为第三方提供。</p>
<ul>
<li><p>内置策略</p>
</li>
<li><ul>
<li>轮询（默认）：Nginx根据请求次数，将每个请求均匀分配到每台服务器；</li>
<li>weight：加权轮询，加权轮询则是在第一种轮询的基础上对后台的每台服务赋予权重，服务器的权重比例越大，被分发到的概率也就越大。</li>
<li>least_conn：最少连接，将请求分配给连接数最少的服务器。Nginx会统计哪些服务器的连接数最少。</li>
<li>ip_hash：IP 哈希，绑定处理请求的服务器。第一次请求时，根据该客户端的IP算出一个HASH值，将请求分配到集群中的某一台服务器上。后面该客户端的所有请求，都将通过HASH算法，找到之前处理这台客户端请求的服务器，然后将请求交给它来处理。</li>
</ul>
</li>
<li><p>扩展策略</p>
</li>
<li><ul>
<li>fair：按后端服务器的响应时间来分配请求，响应时间短的优先分配。</li>
<li>url_hash：按访问url的hash结果来分配请求，使每个url定向到同一个后端服务器，后端服务器为缓存时比较有效。</li>
</ul>
</li>
</ul>
<h3 id="简述Nginx负载均衡（反向代理）通过什么方式实现后端RS的健康检查？"><a href="#简述Nginx负载均衡（反向代理）通过什么方式实现后端RS的健康检查？" class="headerlink" title="简述Nginx负载均衡（反向代理）通过什么方式实现后端RS的健康检查？"></a>简述Nginx负载均衡（反向代理）通过什么方式实现后端RS的健康检查？</h3><p>nginx负载均衡（反向代理）包含内置的或第三方扩展来实现服务器健康检测的。如果后端某台服务器响应失败，nginx会标记该台服务器失效，在特定时间内，请求不分发到该台上。</p>
<ul>
<li>fail_timeout：该指令定义了多长时间服务器将被标记为失败。在fail_timeout后，服务器还是failed，nginx将检测该服务器是否存活，如果探测成功，将标记为活的。</li>
<li>max_fails：该指令设置在fail_timeout期间内连续的失败尝试。默认情况下，max_fails为1。如果被设置为0，该服务器的健康检测将禁用。</li>
</ul>
<h3 id="简述Nginx动静分离？"><a href="#简述Nginx动静分离？" class="headerlink" title="简述Nginx动静分离？"></a>简述Nginx动静分离？</h3><p>为了提高网站的响应速度，减轻程序服务器（Tomcat，Jboss等）的负载，对于静态资源，如图片、js、css等文件，可以在反向代理服务器中进行缓存，这样浏览器在请求一个静态资源时，代理服务器就可以直接处理，而不用将请求转发给后端服务器。对于用户请求的动态文件，如servlet、jsp，则转发给Tomcat，Jboss服务器处理，这就是动静分离。即动态文件与静态文件的分离。</p>
<h3 id="简述Nginx动静分离的原理？"><a href="#简述Nginx动静分离的原理？" class="headerlink" title="简述Nginx动静分离的原理？"></a>简述Nginx动静分离的原理？</h3><p>动静分离可通过location对请求url进行匹配，将网站静态资源（HTML，JavaScript，CSS，img等文件）与后台应用分开部署，提高用户访问静态代码的速度，降低对后台应用访问。通常将静态资源放到nginx中，动态资源转发到tomcat服务器中。</p>
<h3 id="简述Nginx同源策略？"><a href="#简述Nginx同源策略？" class="headerlink" title="简述Nginx同源策略？"></a>简述Nginx同源策略？</h3><p>同源策略是一个安全策略。同源，指的是协议，域名，端口相同。浏览器处于安全方面的考虑，只允许本域名下的接口交互，不同源的客户端脚本，在没有明确授权的情况下，不能读写对方的资源。</p>
<h3 id="简述Nginx跨域及如何实现？"><a href="#简述Nginx跨域及如何实现？" class="headerlink" title="简述Nginx跨域及如何实现？"></a>简述Nginx跨域及如何实现？</h3><p>从一个域名的网页去请求另一个域名的资源，或任何协议、域名、端口有一处不同的请求，就被当作是跨域，即都被当成不同源。</p>
<p>通常基于安全考虑，Nginx启用了同源策略，即限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。</p>
<p>Nginx若要实现跨域访问，可通过JSONP和CORS进行实现。</p>
<h3 id="简述Nginx重定向及其使用的场景？"><a href="#简述Nginx重定向及其使用的场景？" class="headerlink" title="简述Nginx重定向及其使用的场景？"></a>简述Nginx重定向及其使用的场景？</h3><p>重定向(Redirect)指通过各种方法将各种网络请求重新定个方向转到其它位置（如：网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向）。</p>
<p>URL重写是指通过配置conf文件，以让网站的URL中达到某种状态时则定向/跳转到某个规则，比如常见的伪静态、301重定向、浏览器定向等。当客户端浏览某个网址时，将其访问导向到另一个网址的技术。</p>
<p>其主要场景有如下两个：</p>
<ul>
<li>将一串很长的网址，转成较短的网址，从而实现便于传播、易于记忆。</li>
<li>调整或更换Web服务器，网址（域名）又必须要变更（如访问目录、访问扩展名HTML变为PHP、访问域名），为了能使旧的访问依旧生效，从而实现自动重定向到新的网站。</li>
</ul>
<h3 id="简述Nginx地址重写、地址转发、反向代理？"><a href="#简述Nginx地址重写、地址转发、反向代理？" class="headerlink" title="简述Nginx地址重写、地址转发、反向代理？"></a>简述Nginx地址重写、地址转发、反向代理？</h3><ul>
<li><code>地址重写</code>：为了实现地址的标准化，如地址栏中中输入 <a target="_blank" rel="noopener" href="http://www.baidu.com/">www.baidu.com</a>. 也可以输入 <a target="_blank" rel="noopener" href="http://www.baidu.cn.最后都会被重写到/">www.baidu.cn。最后都会被重写到</a> <a target="_blank" rel="noopener" href="http://www.baidu.com/">www.baidu.com</a> 上。浏览器的地址栏也会显示<a target="_blank" rel="noopener" href="http://www.baidu.com.即nginx把收到客户端请求的内容所对应的服务器地址发给客户端,让客户端自己去获取,nginx同时返回302正确信息./">www.baidu.com。即nginx把收到客户端请求的内容所对应的服务器地址发给客户端，让客户端自己去获取，nginx同时返回302正确信息。</a></li>
<li><code>地址转发</code>：指在网络数据传输过程中数据分组到达路由器或桥接器后，该设备通过检查分组地址并将数据转发到最近的局域网的过程。</li>
<li><code>反向代理</code>：当浏览器访问网站时，nginx反向代理服务器会代替客户端向后端服务器查找所需的内容，然后nginx反向代理服务器会把查找的内容返回给客户端。</li>
</ul>
<h3 id="简述Nginx地址重写和地址转发的差异？"><a href="#简述Nginx地址重写和地址转发的差异？" class="headerlink" title="简述Nginx地址重写和地址转发的差异？"></a>简述Nginx地址重写和地址转发的差异？</h3><p>地址重写和地址转发有以下不同点：</p>
<ul>
<li>地址重写会改变浏览器中的地址，使之变成重写成浏览器最新的地址。而地址转发不会改变浏览器的地址的。</li>
<li>地址重写会产生两次请求，而地址转发只会有一次请求。</li>
<li>地址转发一般发生在同一站点项目内部，而地址重写且不受限制。</li>
<li>地址转发的速度比地址重定向快。</li>
</ul>
<h3 id="简述Nginx-301和302重定向及其区别？"><a href="#简述Nginx-301和302重定向及其区别？" class="headerlink" title="简述Nginx 301和302重定向及其区别？"></a>简述Nginx 301和302重定向及其区别？</h3><p>301和302状态码都表示重定向，表示浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址，这个地址可以从响应的Location首部中获取（客户端输入的地址A瞬间变成了另一个地址B）。其主要差异为：</p>
<ul>
<li>301：代表永久性转移(Permanently Moved)：旧地址A的资源已经被永久地移除了（这个资源不可访问了），搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址；</li>
<li>302：代表暂时性转移(Temporarily Moved)：旧地址A的资源还在（仍然可以访问），这个重定向只是临时地从旧地址A跳转到地址B，搜索引擎会抓取新的内容而保存旧的网址。</li>
</ul>
<h3 id="简述Nginx高可用的常见方案？"><a href="#简述Nginx高可用的常见方案？" class="headerlink" title="简述Nginx高可用的常见方案？"></a>简述Nginx高可用的常见方案？</h3><p>Keepalived + Nginx 实现Nginx的高可用：通过Keepalived来实现同一个虚拟IP映射到多台Nginx代理服务器，从而实现Nginx的高可用性。</p>
<p>Heartbeat + Nginx 实现Nginx的高可用：通过Heartbeat的心跳检测和资源接管、集群中服务的监测、失效切换等功能，结合Nginx来实现高可用性。</p>
<h3 id="简述SSL和HTTPS？"><a href="#简述SSL和HTTPS？" class="headerlink" title="简述SSL和HTTPS？"></a>简述SSL和HTTPS？</h3><p><code>SSL</code>（Secure Socket Layer）安全套接字层是一种数字证书，它使用ssl协议在浏览器和web server之间建立一条安全通道，数据信息在client与server之间的安全传输。</p>
<p><code>HTTPS</code>（Hypertext Transfer Protocol Secure）是超文本传输协议和SSL/TLS的组合，用以提供加密通讯及对网络服务器身份的鉴定。</p>
<p>HTTPS也可以理解为HTTP over SSL，即HTTP连接建立在SSL安全连接之上。</p>
<blockquote>
<ul>
<li>作者：木二</li>
<li>链接：<a target="_blank" rel="noopener" href="https://www.yuque.com/docs/share/d3dd1e8e-6828-4da7-9e30-6a4f45c6fa8e">https://www.yuque.com/docs/share/d3dd1e8e-6828-4da7-9e30-6a4f45c6fa8e</a></li>
</ul>
</blockquote>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/interview/" rel="tag">interview</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-k8s/kubeadm高可用master节点三主两从"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/k8s/kubeadm%E9%AB%98%E5%8F%AF%E7%94%A8master%E8%8A%82%E7%82%B9%E4%B8%89%E4%B8%BB%E4%B8%A4%E4%BB%8E/"
    >kubeadm高可用master节点三主两从.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/k8s/kubeadm%E9%AB%98%E5%8F%AF%E7%94%A8master%E8%8A%82%E7%82%B9%E4%B8%89%E4%B8%BB%E4%B8%A4%E4%BB%8E/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/k8s/">k8s</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="kubeadm高可用master节点（三主两从）"><a href="#kubeadm高可用master节点（三主两从）" class="headerlink" title="kubeadm高可用master节点（三主两从）"></a>kubeadm高可用master节点（三主两从）</h1><h2 id="1、安装要求"><a href="#1、安装要求" class="headerlink" title="1、安装要求"></a>1、安装要求</h2><p>在开始之前，部署Kubernetes集群机器需要满足以下几个条件：</p>
<ul>
<li>五台机器，操作系统 CentOS7.5+（mini）</li>
<li>硬件配置：2GBRAM，2vCPU+，硬盘30GB+</li>
<li>集群中所有机器之间网络互通，且可访问外网。</li>
</ul>
<h2 id="2、安装步骤"><a href="#2、安装步骤" class="headerlink" title="2、安装步骤"></a>2、安装步骤</h2><table>
<thead>
<tr>
<th align="left">角色</th>
<th align="left">IP</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>k8s-lb</code></td>
<td align="left">192.168.50.100</td>
</tr>
<tr>
<td align="left"><code>master1</code></td>
<td align="left">192.168.50.128</td>
</tr>
<tr>
<td align="left"><code>master2</code></td>
<td align="left">192.168.50.129</td>
</tr>
<tr>
<td align="left"><code>master3</code></td>
<td align="left">192.168.50.130</td>
</tr>
<tr>
<td align="left"><code>node1</code></td>
<td align="left">192.168.50.131</td>
</tr>
<tr>
<td align="left"><code>node2</code></td>
<td align="left">192.168.50.132</td>
</tr>
</tbody></table>
<h3 id="2-1、安装前预处理操作"><a href="#2-1、安装前预处理操作" class="headerlink" title="2.1、安装前预处理操作"></a>2.1、安装前预处理操作</h3><h4 id="（1）配置主机名"><a href="#（1）配置主机名" class="headerlink" title="（1）配置主机名"></a>（1）配置主机名</h4><p><code>master1</code>节点设置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# hostnamectl set-hostname master1</span><br></pre></td></tr></table></figure>

<p><code>master2</code>节点设置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# hostnamectl set-hostname master2</span><br></pre></td></tr></table></figure>

<p><code>master3</code>节点设置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# hostnamectl set-hostname master3</span><br></pre></td></tr></table></figure>

<p><code>node1</code>从节点设置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# hostnamectl set-hostname node1</span><br></pre></td></tr></table></figure>

<p><code>node2</code>从节点设置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# hostnamectl set-hostname node2</span><br></pre></td></tr></table></figure>

<p><strong>执行<code>bash</code>命令以加载新设置的主机名</strong></p>
<h4 id="（2）添加hosts"><a href="#（2）添加hosts" class="headerlink" title="（2）添加hosts"></a>（2）添加<code>hosts</code></h4><p>所有的节点都要添加<code>hosts</code>解析记录</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">~]# cat &gt;&gt;&#x2F;etc&#x2F;hosts &lt;&lt;EOF</span><br><span class="line">192.168.50.100 k8s-lb</span><br><span class="line">192.168.50.128 master1</span><br><span class="line">192.168.50.129 master2</span><br><span class="line">192.168.50.130 master3</span><br><span class="line">192.168.50.131 node1</span><br><span class="line">192.168.50.132 node2</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure>

<h4 id="（3）配置免密"><a href="#（3）配置免密" class="headerlink" title="（3）配置免密"></a>（3）配置免密</h4><p>在<code>master1</code>节点生成密钥对，并分发给其他的所有主机。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# ssh-keygen -t rsa -b 1200</span><br><span class="line">Generating public&#x2F;private rsa key pair.</span><br><span class="line">Enter file in which to save the key (&#x2F;root&#x2F;.ssh&#x2F;id_rsa): </span><br><span class="line">Enter passphrase (empty for no passphrase): </span><br><span class="line">Enter same passphrase again: </span><br><span class="line">Your identification has been saved in &#x2F;root&#x2F;.ssh&#x2F;id_rsa.</span><br><span class="line">Your public key has been saved in &#x2F;root&#x2F;.ssh&#x2F;id_rsa.pub.</span><br><span class="line">The key fingerprint is:</span><br><span class="line">SHA256:OoMw1dARsWhbJKAQL2hUxwnM4tLQJeLynAQHzqNQs5s root@localhost.localdomain</span><br><span class="line">The key&#39;s randomart image is:</span><br><span class="line">+---[RSA 1200]----+</span><br><span class="line">|*&#x3D;X&#x3D;*o*+         |</span><br><span class="line">|OO.*.O..         |</span><br><span class="line">|BO&#x3D; + +          |</span><br><span class="line">|**o* o           |</span><br><span class="line">|o E .   S        |</span><br><span class="line">|   o . .         |</span><br><span class="line">|    . +          |</span><br><span class="line">|       o         |</span><br><span class="line">|                 |</span><br><span class="line">+----[SHA256]-----+</span><br></pre></td></tr></table></figure>

<h5 id="分发公钥"><a href="#分发公钥" class="headerlink" title="分发公钥"></a>分发公钥</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# ssh-copy-id -i ~&#x2F;.ssh&#x2F;id_rsa.pub  root@master1</span><br><span class="line">[root@master1 ~]# ssh-copy-id -i ~&#x2F;.ssh&#x2F;id_rsa.pub  root@master2</span><br><span class="line">[root@master1 ~]# ssh-copy-id -i ~&#x2F;.ssh&#x2F;id_rsa.pub  root@master3</span><br><span class="line">[root@master1 ~]# ssh-copy-id -i ~&#x2F;.ssh&#x2F;id_rsa.pub  root@node1</span><br><span class="line">[root@master1 ~]# ssh-copy-id -i ~&#x2F;.ssh&#x2F;id_rsa.pub  root@node2</span><br></pre></td></tr></table></figure>

<h4 id="（4）升级内核"><a href="#（4）升级内核" class="headerlink" title="（4）升级内核"></a>（4）升级内核</h4><p>通过下载<code>kernel image</code>的rpm包进行安装。</p>
<p>centos7系统：<code>http://elrepo.org/linux/kernel/el7/x86_64/RPMS/</code></p>
<p><img src="http://iubest.gitee.io/pic/640-1601003120729.webp" alt="img"></p>
<p>编写<code>shell</code>脚本升级内核</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">#!&#x2F;bin&#x2F;bash</span><br><span class="line"># ----------------------------</span><br><span class="line"># upgrade kernel by bomingit@126.com</span><br><span class="line"># ----------------------------</span><br><span class="line"></span><br><span class="line">yum localinstall -y kernel-lt*</span><br><span class="line">if [ $? -eq 0 ];then</span><br><span class="line"> grub2-set-default 0 &amp;&amp; grub2-mkconfig -o &#x2F;etc&#x2F;grub2.cfg</span><br><span class="line"> grubby --args&#x3D;&quot;user_namespace.enable&#x3D;1&quot; --update-kernel&#x3D;&quot;$(grubby --default-kernel)&quot;</span><br><span class="line">fi</span><br><span class="line">echo &quot;please reboot your system quick!!!&quot;</span><br></pre></td></tr></table></figure>

<p>注意：<strong>一定要重启机器</strong></p>
<h5 id="验证内核版本"><a href="#验证内核版本" class="headerlink" title="验证内核版本"></a>验证内核版本</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# uname -r</span><br><span class="line">4.4.229-1.el7.elrepo.x86_64</span><br></pre></td></tr></table></figure>

<h4 id="（5）关闭防火墙selinux"><a href="#（5）关闭防火墙selinux" class="headerlink" title="（5）关闭防火墙selinux"></a>（5）关闭防火墙<code>selinux</code></h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">~]# systemctl disable --now firewalld</span><br><span class="line">~]# setenforce 0</span><br><span class="line">~]# sed -i &#39;s&#x2F;enforcing&#x2F;disabled&#x2F;&#39; &#x2F;etc&#x2F;selinux&#x2F;config </span><br></pre></td></tr></table></figure>

<p>上面的是临时关闭，当然也可以永久关闭，即在<code>/etc/fstab</code>文件中将<code>swap</code>挂载所在的行注释掉即可。</p>
<h4 id="（6）关闭swap分区"><a href="#（6）关闭swap分区" class="headerlink" title="（6）关闭swap分区"></a>（6）关闭<code>swap</code>分区</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">~]# swapoff -a</span><br><span class="line">~]# sed -i.bak &#39;s&#x2F;^.*centos-swap&#x2F;#&amp;&#x2F;g&#39; &#x2F;etc&#x2F;fstab</span><br></pre></td></tr></table></figure>

<p>第一条是临时关闭，当然也可以使用第二条永久关闭，后者手动在<code>/etc/fstab</code>文件中将<code>swap</code>挂载所在的行注释掉即可。</p>
<h4 id="（7）优化内核"><a href="#（7）优化内核" class="headerlink" title="（7）优化内核"></a>（7）优化内核</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">~]# cat &gt; &#x2F;etc&#x2F;sysctl.d&#x2F;k8s.conf &lt;&lt; EOF</span><br><span class="line">net.ipv4.ip_forward &#x3D; 1</span><br><span class="line">net.bridge.bridge-nf-call-iptables &#x3D; 1</span><br><span class="line">net.bridge.bridge-nf-call-ip6tables &#x3D; 1</span><br><span class="line">fs.may_detach_mounts &#x3D; 1</span><br><span class="line">vm.overcommit_memory&#x3D;1</span><br><span class="line">vm.panic_on_oom&#x3D;0</span><br><span class="line">fs.inotify.max_user_watches&#x3D;89100</span><br><span class="line">fs.file-max&#x3D;52706963</span><br><span class="line">fs.nr_open&#x3D;52706963</span><br><span class="line">net.ipv4.tcp_keepalive_time &#x3D; 600</span><br><span class="line">net.ipv4.tcp.keepaliv.probes &#x3D; 3</span><br><span class="line">net.ipv4.tcp_keepalive_intvl &#x3D; 15</span><br><span class="line">net.ipv4.tcp.max_tw_buckets &#x3D; 36000</span><br><span class="line">net.ipv4.tcp_tw_reuse &#x3D; 1</span><br><span class="line">net.ipv4.tcp.max_orphans &#x3D; 327680</span><br><span class="line">net.ipv4.tcp_orphan_retries &#x3D; 3</span><br><span class="line">net.ipv4.tcp_syncookies &#x3D; 1</span><br><span class="line">net.ipv4.tcp_max_syn_backlog &#x3D; 16384</span><br><span class="line">net.ipv4.ip_conntrack_max &#x3D; 65536</span><br><span class="line">net.ipv4.tcp_max_syn_backlog &#x3D; 16384</span><br><span class="line">net.ipv4.top_timestamps &#x3D; 0</span><br><span class="line">net.core.somaxconn &#x3D; 16384</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure>

<p>使其立即生效</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# sysctl --system</span><br></pre></td></tr></table></figure>

<h4 id="（8）配置yum源"><a href="#（8）配置yum源" class="headerlink" title="（8）配置yum源"></a>（8）配置<code>yum</code>源</h4><p>所有的节点均采用阿里云官网的<code>base</code>和<code>epel</code>源</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">~]# mv &#x2F;etc&#x2F;yum.repos.d&#x2F;* &#x2F;tmp</span><br><span class="line">~]# curl -o &#x2F;etc&#x2F;yum.repos.d&#x2F;CentOS-Base.repo https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;repo&#x2F;Centos-7.repo</span><br><span class="line">~]# curl -o &#x2F;etc&#x2F;yum.repos.d&#x2F;epel.repo http:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;repo&#x2F;epel-7.repo</span><br></pre></td></tr></table></figure>

<h4 id="（9）时区与时间同步"><a href="#（9）时区与时间同步" class="headerlink" title="（9）时区与时间同步"></a>（9）时区与时间同步</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">~]# ln -sf &#x2F;usr&#x2F;share&#x2F;zoneinfo&#x2F;Asia&#x2F;Shanghai &#x2F;etc&#x2F;localtime</span><br><span class="line">~]# yum install dnf ntpdate -y</span><br><span class="line">~]# ntpdate ntp.aliyun.com</span><br></pre></td></tr></table></figure>

<h4 id="（10）编写shell"><a href="#（10）编写shell" class="headerlink" title="（10）编写shell"></a>（10）编写<code>shell</code></h4><p>将上面的第5-8步骤写成<code>shell</code>脚本自动化快速完成</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line">#!&#x2F;bin&#x2F;sh</span><br><span class="line">#****************************************************************#</span><br><span class="line"># ScriptName: init.sh</span><br><span class="line"># Author: boming</span><br><span class="line"># Create Date: 2020-06-23 22:19</span><br><span class="line">#***************************************************************#</span><br><span class="line"></span><br><span class="line">#关闭防火墙</span><br><span class="line">systemctl disable --now firewalld</span><br><span class="line">setenforce 0</span><br><span class="line">sed -i &#39;s&#x2F;enforcing&#x2F;disabled&#x2F;&#39; &#x2F;etc&#x2F;selinux&#x2F;config</span><br><span class="line">#关闭swap分区</span><br><span class="line">swapoff -a</span><br><span class="line">sed -i.bak &#39;s&#x2F;^.*centos-swap&#x2F;#&amp;&#x2F;g&#39; &#x2F;etc&#x2F;fstab</span><br><span class="line">#优化系统</span><br><span class="line">cat &gt; &#x2F;etc&#x2F;sysctl.d&#x2F;k8s.conf &lt;&lt; EOF</span><br><span class="line">net.ipv4.ip_forward &#x3D; 1</span><br><span class="line">net.bridge.bridge-nf-call-iptables &#x3D; 1</span><br><span class="line">net.bridge.bridge-nf-call-ip6tables &#x3D; 1</span><br><span class="line">fs.may_detach_mounts &#x3D; 1</span><br><span class="line">vm.overcommit_memory&#x3D;1</span><br><span class="line">vm.panic_on_oom&#x3D;0</span><br><span class="line">fs.inotify.max_user_watches&#x3D;89100</span><br><span class="line">fs.file-max&#x3D;52706963</span><br><span class="line">fs.nr_open&#x3D;52706963</span><br><span class="line">net.ipv4.tcp_keepalive_time &#x3D; 600</span><br><span class="line">net.ipv4.tcp.keepaliv.probes &#x3D; 3</span><br><span class="line">net.ipv4.tcp_keepalive_intvl &#x3D; 15</span><br><span class="line">net.ipv4.tcp.max_tw_buckets &#x3D; 36000</span><br><span class="line">net.ipv4.tcp_tw_reuse &#x3D; 1</span><br><span class="line">net.ipv4.tcp.max_orphans &#x3D; 327680</span><br><span class="line">net.ipv4.tcp_orphan_retries &#x3D; 3</span><br><span class="line">net.ipv4.tcp_syncookies &#x3D; 1</span><br><span class="line">net.ipv4.tcp_max_syn_backlog &#x3D; 16384</span><br><span class="line">net.ipv4.ip_conntrack_max &#x3D; 65536</span><br><span class="line">net.ipv4.tcp_max_syn_backlog &#x3D; 16384</span><br><span class="line">net.ipv4.top_timestamps &#x3D; 0</span><br><span class="line">net.core.somaxconn &#x3D; 16384</span><br><span class="line">EOF</span><br><span class="line">#立即生效</span><br><span class="line">sysctl --system</span><br><span class="line">#配置阿里云的base和epel源</span><br><span class="line">mv &#x2F;etc&#x2F;yum.repos.d&#x2F;* &#x2F;tmp</span><br><span class="line">curl -o &#x2F;etc&#x2F;yum.repos.d&#x2F;CentOS-Base.repo https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;repo&#x2F;Centos-7.repo</span><br><span class="line">curl -o &#x2F;etc&#x2F;yum.repos.d&#x2F;epel.repo http:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;repo&#x2F;epel-7.repo</span><br><span class="line">#安装dnf工具</span><br><span class="line"></span><br><span class="line">yum install dnf -y</span><br><span class="line">dnf makecache</span><br><span class="line">#安装ntpdate工具</span><br><span class="line">dnf install ntpdate -y</span><br><span class="line">#同步阿里云时间</span><br><span class="line">ln -sf &#x2F;usr&#x2F;share&#x2F;zoneinfo&#x2F;Asia&#x2F;Shanghai &#x2F;etc&#x2F;localtime</span><br><span class="line">ntpdate ntp.aliyun.com</span><br></pre></td></tr></table></figure>

<p>在其他的节点执行此脚本跑一下即可。</p>
<h3 id="2-2、安装docker"><a href="#2-2、安装docker" class="headerlink" title="2.2、安装docker"></a>2.2、安装<code>docker</code></h3><h4 id="（1）添加docker软件yum源"><a href="#（1）添加docker软件yum源" class="headerlink" title="（1）添加docker软件yum源"></a>（1）添加<code>docker</code>软件<code>yum</code>源</h4><p>方法：浏览器打开<code>mirrors.aliyun.com</code>网站，找到<code>docker-ce</code>，即可看到镜像仓库源</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003120653.webp" alt="img"></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">~]# curl -o &#x2F;etc&#x2F;yum.repos.d&#x2F;docker-ce.repo https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;docker-ce&#x2F;linux&#x2F;centos&#x2F;docker-ce.repo</span><br><span class="line">~]# cat &#x2F;etc&#x2F;yum.repos.d&#x2F;docker-ce.repo</span><br><span class="line">[docker-ce-stable]</span><br><span class="line">name&#x3D;Docker CE Stable - $basearch</span><br><span class="line">baseurl&#x3D;https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;docker-ce&#x2F;linux&#x2F;centos&#x2F;7&#x2F;$basearch&#x2F;stable</span><br><span class="line">enabled&#x3D;1</span><br><span class="line">gpgcheck&#x3D;1</span><br><span class="line">gpgkey&#x3D;https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;docker-ce&#x2F;linux&#x2F;centos&#x2F;gpg</span><br><span class="line">...</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<h4 id="（2）安装docker-ce组件"><a href="#（2）安装docker-ce组件" class="headerlink" title="（2）安装docker-ce组件"></a>（2）安装<code>docker-ce</code>组件</h4><p>列出所有可以安装的版本</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">~]# dnf list docker-ce --showduplicates</span><br><span class="line">docker-ce.x86_64       3:18.09.6-3.el7               docker-ce-stable</span><br><span class="line">docker-ce.x86_64       3:18.09.7-3.el7               docker-ce-stable</span><br><span class="line">docker-ce.x86_64       3:18.09.8-3.el7               docker-ce-stable</span><br><span class="line">docker-ce.x86_64       3:18.09.9-3.el7               docker-ce-stable</span><br><span class="line">docker-ce.x86_64       3:19.03.0-3.el7               docker-ce-stable</span><br><span class="line">docker-ce.x86_64       3:19.03.1-3.el7               docker-ce-stable</span><br><span class="line">docker-ce.x86_64       3:19.03.2-3.el7               docker-ce-stable</span><br><span class="line">docker-ce.x86_64       3:19.03.3-3.el7               docker-ce-stable</span><br><span class="line">docker-ce.x86_64       3:19.03.4-3.el7               docker-ce-stable</span><br><span class="line">docker-ce.x86_64       3:19.03.5-3.el7               docker-ce-stable</span><br><span class="line">.....</span><br></pre></td></tr></table></figure>

<p>这里我们安装最新版本的<code>docker</code>，所有的节点都需要安装<code>docker</code>服务</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# dnf install -y  docker-ce docker-ce-cli</span><br></pre></td></tr></table></figure>

<h4 id="（3）启动docker并设置开机自启动"><a href="#（3）启动docker并设置开机自启动" class="headerlink" title="（3）启动docker并设置开机自启动"></a>（3）启动<code>docker</code>并设置开机自启动</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# systemctl enable --now docker</span><br></pre></td></tr></table></figure>

<p>查看版本号，检测<code>docker</code>是否安装成功</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">~]# docker --version</span><br><span class="line">Docker version 19.03.12, build 48a66213fea</span><br></pre></td></tr></table></figure>

<p>上面的这种查看<code>docker client</code>的版本的。建议使用下面这种方法查看<code>docker-ce</code>版本号，这种方法把<code>docker</code>的<code>client</code>端和<code>server</code>端的版本号查看的一清二楚。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">~]# docker version</span><br><span class="line">Client:</span><br><span class="line"> Version:           19.03.12</span><br><span class="line"> API version:       1.40</span><br><span class="line"> Go version:        go1.13.10</span><br><span class="line"> Git commit:        039a7df9ba</span><br><span class="line"> Built:             Wed Sep  4 16:51:21 2019</span><br><span class="line"> OS&#x2F;Arch:           linux&#x2F;amd64</span><br><span class="line"> Experimental:      false</span><br><span class="line"></span><br><span class="line">Server: Docker Engine - Community</span><br><span class="line"> Engine:</span><br><span class="line">  Version:          19.03.12</span><br><span class="line">  API version:      1.40 (minimum version 1.12)</span><br><span class="line">  Go version:       go1.13.10</span><br><span class="line">  Git commit:       039a7df</span><br><span class="line">  Built:            Wed Sep  4 16:22:32 2019</span><br><span class="line">  OS&#x2F;Arch:          linux&#x2F;amd64</span><br><span class="line">  Experimental:     false</span><br></pre></td></tr></table></figure>

<h4 id="（4）更换docker的镜像仓库源"><a href="#（4）更换docker的镜像仓库源" class="headerlink" title="（4）更换docker的镜像仓库源"></a>（4）更换<code>docker</code>的镜像仓库源</h4><p>默认的镜像仓库地址是<code>docker</code>官方的，国内访问异常缓慢，因此更换为个人阿里云的源。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">~]# cat &gt; &#x2F;etc&#x2F;docker&#x2F;daemon.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">  &quot;registry-mirrors&quot;: [&quot;https:&#x2F;&#x2F;f1bhsuge.mirror.aliyuncs.com&quot;]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure>

<p>由于重新加载<code>docker仓库源</code>，所以需要重启<code>docker</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# systemctl restart docker</span><br></pre></td></tr></table></figure>

<h3 id="2-3、安装kubernetes"><a href="#2-3、安装kubernetes" class="headerlink" title="2.3、安装kubernetes"></a>2.3、安装<code>kubernetes</code></h3><h4 id="（1）添加kubernetes软件yum源"><a href="#（1）添加kubernetes软件yum源" class="headerlink" title="（1）添加kubernetes软件yum源"></a>（1）添加<code>kubernetes</code>软件<code>yum</code>源</h4><p>方法：浏览器打开<code>mirrors.aliyun.com</code>网站，找到<code>kubernetes</code>，即可看到镜像仓库源</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003120790.webp" alt="img"></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">~]# cat &gt; &#x2F;etc&#x2F;yum.repos.d&#x2F;kubernetes.repo &lt;&lt; EOF</span><br><span class="line">[kubernetes]</span><br><span class="line">name&#x3D;Kubernetes</span><br><span class="line">baseurl&#x3D;https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;kubernetes&#x2F;yum&#x2F;repos&#x2F;kubernetes-el7-x86_64</span><br><span class="line">enabled&#x3D;1</span><br><span class="line">gpgcheck&#x3D;0</span><br><span class="line">repo_gpgcheck&#x3D;0</span><br><span class="line">gpgkey&#x3D;https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;kubernetes&#x2F;yum&#x2F;doc&#x2F;yum-key.gpg https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;kubernetes&#x2F;yum&#x2F;doc&#x2F;rpm-package-key.gpg</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure>

<p>最好是重新生成缓存</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">~]# dnf clean all</span><br><span class="line">~]# dnf makecache</span><br></pre></td></tr></table></figure>

<h4 id="（2）安装kubeadm、kubelet和kubectl组件"><a href="#（2）安装kubeadm、kubelet和kubectl组件" class="headerlink" title="（2）安装kubeadm、kubelet和kubectl组件"></a>（2）安装<code>kubeadm</code>、<code>kubelet</code>和<code>kubectl</code>组件</h4><p>所有的节点都需要安装这几个组件。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# dnf list kubeadm --showduplicates</span><br><span class="line">kubeadm.x86_64                       1.17.7-0                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.17.7-1                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.17.8-0                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.17.9-0                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.18.0-0                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.18.1-0                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.18.2-0                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.18.3-0                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.18.4-0                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.18.4-1                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.18.5-0                     kubernetes</span><br><span class="line">kubeadm.x86_64                       1.18.6-0                     kubernetes</span><br></pre></td></tr></table></figure>

<p>由于kubernetes版本变更非常快，因此列出有哪些版本，选择一个合适的。我们这里安装<code>1.18.6</code>版本。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# dnf install -y kubelet-1.18.6 kubeadm-1.18.6 kubectl-1.18.6</span><br></pre></td></tr></table></figure>

<h4 id="（3）设置开机自启动"><a href="#（3）设置开机自启动" class="headerlink" title="（3）设置开机自启动"></a>（3）设置开机自启动</h4><blockquote>
<p>我们先设置开机自启，但是<code>kubelet</code>服务暂时先不启动。</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# systemctl enable kubelet</span><br></pre></td></tr></table></figure>

<h3 id="2-4、Haproxy-Keepalived配置高可用VIP"><a href="#2-4、Haproxy-Keepalived配置高可用VIP" class="headerlink" title="2.4、Haproxy+Keepalived配置高可用VIP"></a>2.4、<code>Haproxy+Keepalived</code>配置高可用VIP</h3><p>高可用我们采用官方推荐的<code>HAproxy+Keepalived</code>，<code>HAproxy</code>和<code>Keepalived</code>以守护进程的方式在所有<code>Master</code>节点部署。</p>
<h4 id="（1）安装keepalived和haproxy"><a href="#（1）安装keepalived和haproxy" class="headerlink" title="（1）安装keepalived和haproxy"></a>（1）安装<code>keepalived</code>和<code>haproxy</code></h4><p>注意：只需要在三个<code>master</code>节点安装即可</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# dnf install -y keepalived haproxy </span><br></pre></td></tr></table></figure>

<h4 id="（2）配置Haproxy服务"><a href="#（2）配置Haproxy服务" class="headerlink" title="（2）配置Haproxy服务"></a>（2）配置<code>Haproxy</code>服务</h4><p>所有<code>master</code>节点的<code>haproxy</code>配置相同，haproxy的配置文件是<code>/etc/haproxy/haproxy.cfg</code>。<code>master1</code>节点配置完成之后再分发给<code>master2、master3</code>两个节点。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">global</span><br><span class="line">  maxconn  2000</span><br><span class="line">  ulimit-n  16384</span><br><span class="line">  log  127.0.0.1 local0 err</span><br><span class="line">  stats timeout 30s</span><br><span class="line"></span><br><span class="line">defaults</span><br><span class="line">  log global</span><br><span class="line">  mode  http</span><br><span class="line">  option  httplog</span><br><span class="line">  timeout connect 5000</span><br><span class="line">  timeout client  50000</span><br><span class="line">  timeout server  50000</span><br><span class="line">  timeout http-request 15s</span><br><span class="line">  timeout http-keep-alive 15s</span><br><span class="line"></span><br><span class="line">frontend monitor-in</span><br><span class="line">  bind *:33305</span><br><span class="line">  mode http</span><br><span class="line">  option httplog</span><br><span class="line">  monitor-uri &#x2F;monitor</span><br><span class="line"></span><br><span class="line">listen stats</span><br><span class="line">  bind    *:8006</span><br><span class="line">  mode    http</span><br><span class="line">  stats   enable</span><br><span class="line">  stats   hide-version</span><br><span class="line">  stats   uri       &#x2F;stats</span><br><span class="line">  stats   refresh   30s</span><br><span class="line">  stats   realm     Haproxy\ Statistics</span><br><span class="line">  stats   auth      admin:admin</span><br><span class="line"></span><br><span class="line">frontend k8s-master</span><br><span class="line">  bind 0.0.0.0:8443</span><br><span class="line">  bind 127.0.0.1:8443</span><br><span class="line">  mode tcp</span><br><span class="line">  option tcplog</span><br><span class="line">  tcp-request inspect-delay 5s</span><br><span class="line">  default_backend k8s-master</span><br><span class="line"></span><br><span class="line">backend k8s-master</span><br><span class="line">  mode tcp</span><br><span class="line">  option tcplog</span><br><span class="line">  option tcp-check</span><br><span class="line">  balance roundrobin</span><br><span class="line">  default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100</span><br><span class="line">  server master1 192.168.50.128:6443  check inter 2000 fall 2 rise 2 weight 100</span><br><span class="line">  server master2 192.168.50.129:6443  check inter 2000 fall 2 rise 2 weight 100</span><br><span class="line">  server master3 192.168.50.130:6443  check inter 2000 fall 2 rise 2 weight 100</span><br></pre></td></tr></table></figure>

<p>注意这里的三个master节点的ip地址要根据你自己的情况配置好。</p>
<h4 id="（3）配置Keepalived服务"><a href="#（3）配置Keepalived服务" class="headerlink" title="（3）配置Keepalived服务"></a>（3）配置<code>Keepalived</code>服务</h4><p><code>keepalived</code>中使用<code>track_script</code>机制来配置脚本进行探测<code>kubernetes</code>的<code>master</code>节点是否宕机，并以此切换节点实现高可用。</p>
<p><code>master1</code>节点的<code>keepalived</code>配置文件如下所示，配置文件所在的位置<code>/etc/keepalived/keepalived.cfg</code>。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">! Configuration File for keepalived</span><br><span class="line">global_defs &#123;</span><br><span class="line">    router_id LVS_DEVEL</span><br><span class="line">&#125;</span><br><span class="line">vrrp_script chk_kubernetes &#123;</span><br><span class="line">    script &quot;&#x2F;etc&#x2F;keepalived&#x2F;check_kubernetes.sh&quot;</span><br><span class="line">    interval 2</span><br><span class="line">    weight -5</span><br><span class="line">    fall 3  </span><br><span class="line">    rise 2</span><br><span class="line">&#125;</span><br><span class="line">vrrp_instance VI_1 &#123;</span><br><span class="line">    state MASTER</span><br><span class="line">    interface ens33</span><br><span class="line">    mcast_src_ip 192.168.50.128</span><br><span class="line">    virtual_router_id 51</span><br><span class="line">    priority 100</span><br><span class="line">    advert_int 2</span><br><span class="line">    authentication &#123;</span><br><span class="line">        auth_type PASS</span><br><span class="line">        auth_pass K8SHA_KA_AUTH</span><br><span class="line">    &#125;</span><br><span class="line">    virtual_ipaddress &#123;</span><br><span class="line">        192.168.50.100</span><br><span class="line">    &#125;</span><br><span class="line">#    track_script &#123;</span><br><span class="line">#       chk_kubernetes</span><br><span class="line">#    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>需要注意几点（前两点记得修改）：</p>
<ul>
<li><code>mcast_src_ip</code>：配置多播源地址，此地址是当前主机的ip地址。</li>
<li><code>priority</code>：<code>keepalived</code>根据此项参数的大小仲裁<code>master</code>节点。我们这里让master节点为<code>kubernetes</code>提供服务，其他两个节点暂时为备用节点。因此<code>master1</code>节点设置为<code>100</code>，<code>master2</code>节点设置为<code>99</code>，<code>master3</code>节点设置为<code>98</code>。</li>
<li><code>state</code>：我们将<code>master1</code>节点的<code>state</code>字段设置为<code>MASTER</code>，其他两个节点字段修改为<code>BACKUP</code>。</li>
<li>上面的集群检查功能是关闭的，等到集群建立完成后再开启。</li>
</ul>
<h4 id="（4）配置健康检测脚本"><a href="#（4）配置健康检测脚本" class="headerlink" title="（4）配置健康检测脚本"></a>（4）配置健康检测脚本</h4><p>我这里将健康检测脚本放置在<code>/etc/keepalived</code>目录下，<code>check_kubernetes.sh</code>检测脚本如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">#!&#x2F;bin&#x2F;bash</span><br><span class="line">#****************************************************************#</span><br><span class="line"># ScriptName: check_kubernetes.sh</span><br><span class="line"># Author: boming</span><br><span class="line"># Create Date: 2020-06-23 22:19</span><br><span class="line">#***************************************************************#</span><br><span class="line"></span><br><span class="line">function chech_kubernetes() &#123;</span><br><span class="line"> for ((i&#x3D;0;i&lt;5;i++));do</span><br><span class="line">  apiserver_pid_id&#x3D;$(pgrep kube-apiserver)</span><br><span class="line">  if [[ ! -z $apiserver_pid_id ]];then</span><br><span class="line">   return</span><br><span class="line">  else</span><br><span class="line">   sleep 2</span><br><span class="line">  fi</span><br><span class="line">  apiserver_pid_id&#x3D;0</span><br><span class="line"> done</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"># 1:running  0:stopped</span><br><span class="line">check_kubernetes</span><br><span class="line">if [[ $apiserver_pid_id -eq 0 ]];then</span><br><span class="line"> &#x2F;usr&#x2F;bin&#x2F;systemctl stop keepalived</span><br><span class="line"> exit 1</span><br><span class="line">else</span><br><span class="line"> exit 0</span><br><span class="line">fi</span><br></pre></td></tr></table></figure>

<p>根据上面的注意事项配置<code>master2</code>、<code>master3</code>节点的<code>keepalived</code>服务。</p>
<h4 id="（5）启动Keeplived和Haproxy服务"><a href="#（5）启动Keeplived和Haproxy服务" class="headerlink" title="（5）启动Keeplived和Haproxy服务"></a>（5）启动<code>Keeplived</code>和<code>Haproxy</code>服务</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# systemctl enable --now keepalived haproxy</span><br></pre></td></tr></table></figure>

<p>确保万一，查看一下服务状态</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">~]# systemctl status keepalived haproxy</span><br><span class="line">~]# ping 192.168.50.100                    #检测一下是否通</span><br><span class="line">PING 192.168.50.100 (192.168.50.100) 56(84) bytes of data.</span><br><span class="line">64 bytes from 192.168.50.100: icmp_seq&#x3D;1 ttl&#x3D;64 time&#x3D;0.778 ms</span><br><span class="line">64 bytes from 192.168.50.100: icmp_seq&#x3D;2 ttl&#x3D;64 time&#x3D;0.339 ms</span><br></pre></td></tr></table></figure>

<h3 id="2-5、部署Master节点"><a href="#2-5、部署Master节点" class="headerlink" title="2.5、部署Master节点"></a>2.5、部署<code>Master</code>节点</h3><h4 id="（1）生成预处理文件"><a href="#（1）生成预处理文件" class="headerlink" title="（1）生成预处理文件"></a>（1）生成预处理文件</h4><p>在<code>master</code>节点执行如下指令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubeadm config print init-defaults &gt; kubeadm-init.yaml</span><br></pre></td></tr></table></figure>

<p>这个文件<code>kubeadm-init.yaml</code>，是我们初始化使用的文件，里面大概修改这几项参数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# cat kubeadm-init.yaml </span><br><span class="line">apiVersion: kubeadm.k8s.io&#x2F;v1beta2</span><br><span class="line">bootstrapTokens:</span><br><span class="line">- groups:</span><br><span class="line">  - system:bootstrappers:kubeadm:default-node-token</span><br><span class="line">  token: abcdef.0123456789abcdef</span><br><span class="line">  ttl: 24h0m0s</span><br><span class="line">  usages:</span><br><span class="line">  - signing</span><br><span class="line">  - authentication</span><br><span class="line">kind: InitConfiguration</span><br><span class="line">localAPIEndpoint:</span><br><span class="line">  advertiseAddress: 192.168.50.100      #VIP的地址</span><br><span class="line">  bindPort:  6443</span><br><span class="line">nodeRegistration:</span><br><span class="line">  criSocket: &#x2F;var&#x2F;run&#x2F;dockershim.sock</span><br><span class="line">  name: master1</span><br><span class="line">  taints:</span><br><span class="line">  - effect: NoSchedule</span><br><span class="line">    key: node-role.kubernetes.io&#x2F;master</span><br><span class="line">---</span><br><span class="line">apiServer:            #添加如下两行信息</span><br><span class="line">  certSANs:</span><br><span class="line">  - &quot;192.168.50.100&quot;         #VIP地址</span><br><span class="line">  timeoutForControlPlane: 4m0s</span><br><span class="line">apiVersion: kubeadm.k8s.io&#x2F;v1beta2</span><br><span class="line">certificatesDir: &#x2F;etc&#x2F;kubernetes&#x2F;pki</span><br><span class="line">clusterName: kubernetes</span><br><span class="line">controllerManager: &#123;&#125;</span><br><span class="line">dns:</span><br><span class="line">  type: CoreDNS</span><br><span class="line">etcd:</span><br><span class="line">  local:</span><br><span class="line">    dataDir: &#x2F;var&#x2F;lib&#x2F;etcd</span><br><span class="line">imageRepository: registry.cn-hangzhou.aliyuncs.com&#x2F;google_containers   #阿里云的镜像站点</span><br><span class="line">controlPlaneEndpoint: &quot;192.168.50.100:8443&quot;    #VIP的地址和端口</span><br><span class="line">kind: ClusterConfiguration</span><br><span class="line">kubernetesVersion: v1.18.3        #kubernetes版本号</span><br><span class="line">networking:</span><br><span class="line">  dnsDomain: cluster.local </span><br><span class="line">  serviceSubnet: 10.96.0.0&#x2F;12       #选择默认即可，当然也可以自定义CIDR</span><br><span class="line">  podSubnet: 10.244.0.0&#x2F;16        #添加pod网段</span><br><span class="line">scheduler: &#123;&#125;</span><br></pre></td></tr></table></figure>

<p><strong>注意：</strong>上面的<code>advertiseAddress</code>字段的值，这个值并非当前主机的网卡地址，而是高可用集群的<code>VIP</code>的地址。</p>
<p><strong>注意：</strong>上面的<code>controlPlaneEndpoint</code>这里填写的是<code>VIP</code>的地址，而端口则是<code>haproxy</code>服务的<code>8443</code>端口，也就是我们在<code>haproxy</code>里面配置的这段信息。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">frontend k8s-master</span><br><span class="line">  bind 0.0.0.0:8443</span><br><span class="line">  bind 127.0.0.1:8443</span><br><span class="line">  mode tcp</span><br></pre></td></tr></table></figure>

<p>这一段里面的<code>8443</code>端，如果你自定义了其他端口，这里请记得修改<code>controlPlaneEndpoint</code>里面的端口。</p>
<h4 id="（2）提前拉取镜像"><a href="#（2）提前拉取镜像" class="headerlink" title="（2）提前拉取镜像"></a>（2）提前拉取镜像</h4><p>如果直接采用<code>kubeadm init</code>来初始化，中间会有系统自动拉取镜像的这一步骤，这是比较慢的，我建议分开来做，所以这里就先提前拉取镜像。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubeadm config images pull --config kubeadm-init.yaml</span><br><span class="line">[config&#x2F;images] Pulled registry.aliyuncs.com&#x2F;google_containers&#x2F;kube-apiserver:v1.18.0</span><br><span class="line">[config&#x2F;images] Pulled registry.aliyuncs.com&#x2F;google_containers&#x2F;kube-controller-manager:v1.18.0</span><br><span class="line">[config&#x2F;images] Pulled registry.aliyuncs.com&#x2F;google_containers&#x2F;kube-scheduler:v1.18.0</span><br><span class="line">[config&#x2F;images] Pulled registry.aliyuncs.com&#x2F;google_containers&#x2F;kube-proxy:v1.18.0</span><br><span class="line">[config&#x2F;images] Pulled registry.aliyuncs.com&#x2F;google_containers&#x2F;pause:3.1</span><br><span class="line">[config&#x2F;images] Pulled registry.aliyuncs.com&#x2F;google_containers&#x2F;etcd:3.4.3-0</span><br><span class="line">[config&#x2F;images] Pulled registry.aliyuncs.com&#x2F;google_containers&#x2F;coredns:1.6.5</span><br></pre></td></tr></table></figure>

<p>如果大家看到开头的两行<code>warning</code>信息（我这里没有打印），不必担心，这只是警告，不影响我们完成实验。</p>
<h5 id="其他master节点提前拉取镜像"><a href="#其他master节点提前拉取镜像" class="headerlink" title="其他master节点提前拉取镜像"></a>其他<code>master</code>节点提前拉取镜像</h5><p>其他两个<code>master</code>节点在初始化之前也尽量先把镜像拉取下来，这样子减少初始化时间</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# scp kubeadm-init.yaml root@master2:~</span><br><span class="line">[root@master1 ~]# scp kubeadm-init.yaml root@master3:~</span><br></pre></td></tr></table></figure>

<p><strong><code>master2</code>节点</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 注意在master2节点执行如下命令</span><br><span class="line">[root@master2 ~]# kubeadm config images pull --config kubeadm-init.yaml</span><br></pre></td></tr></table></figure>

<p><strong><code>master3</code>节点</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 注意在master3节点执行如下命令</span><br><span class="line">[root@master3 ~]# kubeadm config images pull --config kubeadm-init.yaml</span><br></pre></td></tr></table></figure>

<h4 id="（3）初始化kubenetes的master1节点"><a href="#（3）初始化kubenetes的master1节点" class="headerlink" title="（3）初始化kubenetes的master1节点"></a>（3）初始化<code>kubenetes</code>的<code>master1</code>节点</h4><p>执行如下命令</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubeadm init --config kubeadm-init.yaml --upload-certs</span><br><span class="line">[init] Using Kubernetes version: v1.18.3</span><br><span class="line">[preflight] Running pre-flight checks</span><br><span class="line"> [WARNING IsDockerSystemdCheck]: detected &quot;cgroupfs&quot; as the Docker cgroup driver. The recommended driver is &quot;systemd&quot;. Please follow the guide at https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;setup&#x2F;cri&#x2F;</span><br><span class="line">[preflight] Pulling images required for setting up a Kubernetes cluster</span><br><span class="line">[preflight] This might take a minute or two, depending on the speed of your internet connection</span><br><span class="line">[preflight] You can also perform this action in beforehand using &#39;kubeadm config images pull&#39;</span><br><span class="line">[kubelet-start] Writing kubelet environment file with flags to file &quot;&#x2F;var&#x2F;lib&#x2F;kubelet&#x2F;kubeadm-flags.env&quot;</span><br><span class="line">[kubelet-start] Writing kubelet configuration to file &quot;&#x2F;var&#x2F;lib&#x2F;kubelet&#x2F;config.yaml&quot;</span><br><span class="line">[certs] apiserver serving cert is signed for DNS names [master1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.50.128 192.168.50.100]</span><br><span class="line">...           # 省略</span><br><span class="line">[upload-certs] Skipping phase. Please see --upload-certs</span><br><span class="line">[mark-control-plane] Marking the node master1 as control-plane by adding the label &quot;node-role.kubernetes.io&#x2F;master&#x3D;&#39;&#39;&quot;</span><br><span class="line">[mark-control-plane] Marking the node master1 as control-plane by adding the taints [node-role.kubernetes.io&#x2F;master:NoSchedule]</span><br><span class="line">[addons] Applied essential addon: CoreDNS</span><br><span class="line">[endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address</span><br><span class="line">[addons] Applied essential addon: kube-proxy</span><br><span class="line"></span><br><span class="line">Your Kubernetes control-plane has initialized successfully!</span><br><span class="line"></span><br><span class="line">To start using your cluster, you need to run the following as a regular user:</span><br><span class="line"></span><br><span class="line">  mkdir -p $HOME&#x2F;.kube</span><br><span class="line">  sudo cp -i &#x2F;etc&#x2F;kubernetes&#x2F;admin.conf $HOME&#x2F;.kube&#x2F;config</span><br><span class="line">  sudo chown $(id -u):$(id -g) $HOME&#x2F;.kube&#x2F;config</span><br><span class="line"></span><br><span class="line">You should now deploy a pod network to the cluster.</span><br><span class="line">Run &quot;kubectl apply -f [podnetwork].yaml&quot; with one of the options listed at:</span><br><span class="line">  https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;cluster-administration&#x2F;addons&#x2F;</span><br><span class="line"></span><br><span class="line">You can now join any number of control-plane nodes by copying certificate authorities</span><br><span class="line">and service account keys on each node and then running the following as root:</span><br><span class="line"></span><br><span class="line">  kubeadm join 192.168.50.100:8443 --token abcdef.0123456789abcdef \</span><br><span class="line">    --discovery-token-ca-cert-hash sha256:4c738bc8e2684c5d52d80687d48925613b66ab660403649145eb668d71d85648 \</span><br><span class="line">    --control-plane --certificate-key 4931f39d3f53351cb6966a9dcc53cb5cbd2364c6d5b83e50e258c81fbec69539 </span><br><span class="line"></span><br><span class="line">Then you can join any number of worker nodes by running the following on each as root:</span><br><span class="line"></span><br><span class="line">kubeadm join 192.168.50.100:8443 --token abcdef.0123456789abcdef \</span><br><span class="line">    --discovery-token-ca-cert-hash sha256:4c738bc8e2684c5d52d80687d48925613b66ab660403649145eb668d71d85648</span><br></pre></td></tr></table></figure>

<p>这个过程大概<code>30s</code>的时间就做完了，之所以初始化的这么快就是因为我们提前拉取了镜像。像我上面这样的没有报错信息，并且显示上面的最后10行类似的信息这些，说明我们的<code>master1</code>节点是初始化成功的。</p>
<p>在使用集群之前还需要做些收尾工作，在<code>master1</code>节点执行：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# mkdir -p $HOME&#x2F;.kube</span><br><span class="line">[root@master1 ~]# sudo cp -i &#x2F;etc&#x2F;kubernetes&#x2F;admin.conf $HOME&#x2F;.kube&#x2F;config</span><br><span class="line">[root@master1 ~]# sudo chown $(id -u):$(id -g) $HOME&#x2F;.kube&#x2F;config</span><br></pre></td></tr></table></figure>

<p>再配置一下环境变量</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# cat &gt;&gt; ~&#x2F;.bashrc &lt;&lt;EOF</span><br><span class="line">export KUBECONFIG&#x3D;&#x2F;etc&#x2F;kubernetes&#x2F;admin.conf</span><br><span class="line">EOF</span><br><span class="line">[root@master1 ~]# source ~&#x2F;.bashrc</span><br></pre></td></tr></table></figure>

<p>好了，此时的<code>master1</code>节点就算是初始化完毕了。</p>
<p>有个重要的点就是最后几行信息中，其中有两条<code>kubeadm join 192.168.50.100:8443</code> 开头的信息。这分别是其他<code>master</code>节点和<code>node</code>节点加入<code>kubernetes</code>集群的认证命令。这个密钥是系统根据<code>sha256</code>算法计算出来的，必须持有这样的密钥才可以加入当前的<code>kubernetes</code>集群。</p>
<h5 id="使用区别"><a href="#使用区别" class="headerlink" title="使用区别"></a>使用区别</h5><p>这两条加入集群的命令是有一些区别的：</p>
<p>比如这个第一条，我们看到最后有一行内容<code>--control-plane --certificate-key xxxx</code>，这是控制节点加入集群的命令，控制节点是<code>kubernetes</code>官方的说法，其实在我们这里指的就是其他的<code>master</code>节点。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">kubeadm join 192.168.50.100:8443 --token abcdef.0123456789abcdef \</span><br><span class="line">    --discovery-token-ca-cert-hash sha256:4c738bc8e2684c5d52d80687d48925613b66ab660403649145eb668d71d85648 \</span><br><span class="line">    --control-plane --certificate-key 4931f39d3f53351cb6966a9dcc53cb5cbd2364c6d5b83e50e258c81fbec69539</span><br></pre></td></tr></table></figure>

<p>而最后一条就表示<code>node</code>节点加入集群的命令，比如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">kubeadm join 192.168.50.100:8443 --token abcdef.0123456789abcdef \</span><br><span class="line">    --discovery-token-ca-cert-hash sha256:4c738bc8e2684c5d52d80687d48925613b66ab660403649145eb668d71d85648</span><br></pre></td></tr></table></figure>

<p>所以这两个节点使用时要看清楚是什么类型的节点加入集群的。</p>
<h5 id="查看节点"><a href="#查看节点" class="headerlink" title="查看节点"></a>查看节点</h5><p>如果此时查看当前集群的节点，会发现只有<code>master1</code>节点自己。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl get node</span><br><span class="line">NAME      STATUS     ROLES    AGE     VERSION</span><br><span class="line">master1   NotReady   master   9m58s   v1.18.4</span><br></pre></td></tr></table></figure>

<p>接下来我们把其他两个<code>master</code>节点加入到<code>kubernetes</code>集群中</p>
<h3 id="2-6、其他master节点加入kubernetes集群中"><a href="#2-6、其他master节点加入kubernetes集群中" class="headerlink" title="2.6、其他master节点加入kubernetes集群中"></a>2.6、其他<code>master</code>节点加入<code>kubernetes</code>集群中</h3><h4 id="（1）master2节点加入集群"><a href="#（1）master2节点加入集群" class="headerlink" title="（1）master2节点加入集群"></a>（1）<code>master2</code>节点加入集群</h4><p>既然是其他的master节点加入集群，那肯定是使用如下命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">[root@master2 ~]#  kubeadm join 192.168.50.100:8443 --token abcdef.0123456789abcdef \</span><br><span class="line">     --discovery-token-ca-cert-hash sha256:4c738bc8e2684c5d52d80687d48925613b66ab660403649145eb668d71d85648 \</span><br><span class="line">     --control-plane --certificate-key 4931f39d3f53351cb6966a9dcc53cb5cbd2364c6d5b83e50e258c81fbec69539</span><br><span class="line"></span><br><span class="line">[preflight] Running pre-flight checks</span><br><span class="line"> [WARNING IsDockerSystemdCheck]: detected &quot;cgroupfs&quot; as the Docker cgroup driver. The </span><br><span class="line">......                                  #省略若干</span><br><span class="line">[mark-control-plane] Marking the node master2 as control-plane by adding the taints [node-role.kubernetes.io&#x2F;master:NoSchedule]</span><br><span class="line"></span><br><span class="line">This node has joined the cluster and a new control plane instance was created:</span><br><span class="line"></span><br><span class="line">To start administering your cluster from this node, you need to run the following as a regular user:</span><br><span class="line"></span><br><span class="line"> mkdir -p $HOME&#x2F;.kube</span><br><span class="line"> sudo cp -i &#x2F;etc&#x2F;kubernetes&#x2F;admin.conf $HOME&#x2F;.kube&#x2F;config</span><br><span class="line"> sudo chown $(id -u):$(id -g) $HOME&#x2F;.kube&#x2F;config</span><br><span class="line"></span><br><span class="line">Run &#39;kubectl get nodes&#39; to see this node join the cluster.</span><br></pre></td></tr></table></figure>

<p>看上去没有报错，说明加入集群成功，现在再执行一些收尾工作</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@master2 ~]# mkdir -p $HOME&#x2F;.kube</span><br><span class="line">[root@master2 ~]# sudo cp -i &#x2F;etc&#x2F;kubernetes&#x2F;admin.conf $HOME&#x2F;.kube&#x2F;config</span><br><span class="line">[root@master2 ~]# sudo chown $(id -u):$(id -g) $HOME&#x2F;.kube&#x2F;config</span><br></pre></td></tr></table></figure>

<p>加环境变量</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@master2 ~]# cat &gt;&gt; ~&#x2F;.bashrc &lt;&lt;EOF</span><br><span class="line">export KUBECONFIG&#x3D;&#x2F;etc&#x2F;kubernetes&#x2F;admin.conf</span><br><span class="line">EOF</span><br><span class="line">[root@master2 ~]# source ~&#x2F;.bashrc</span><br></pre></td></tr></table></figure>

<h4 id="（2）master3节点加入集群"><a href="#（2）master3节点加入集群" class="headerlink" title="（2）master3节点加入集群"></a>（2）<code>master3</code>节点加入集群</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@master3 ~]#  kubeadm join 192.168.50.100:8443 --token abcdef.0123456789abcdef \</span><br><span class="line">     --discovery-token-ca-cert-hash sha256:4c738bc8e2684c5d52d80687d48925613b66ab660403649145eb668d71d85648 \</span><br><span class="line">     --control-plane --certificate-key 4931f39d3f53351cb6966a9dcc53cb5cbd2364c6d5b83e50e258c81fbec69539</span><br></pre></td></tr></table></figure>

<p>做一些收尾工作</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[root@master3 ~]# mkdir -p $HOME&#x2F;.kube</span><br><span class="line">[root@master3 ~]# sudo cp -i &#x2F;etc&#x2F;kubernetes&#x2F;admin.conf $HOME&#x2F;.kube&#x2F;config</span><br><span class="line">[root@master3 ~]# sudo chown $(id -u):$(id -g) $HOME&#x2F;.kube&#x2F;config</span><br><span class="line">[root@master3 ~]# cat &gt;&gt; ~&#x2F;.bashrc &lt;&lt;EOF</span><br><span class="line">export KUBECONFIG&#x3D;&#x2F;etc&#x2F;kubernetes&#x2F;admin.conf</span><br><span class="line">EOF</span><br><span class="line">[root@master3 ~]# source ~&#x2F;.bashrc</span><br></pre></td></tr></table></figure>

<p>到此，所有的<code>master</code>节点都已经加入集群</p>
<h5 id="查看集群master节点"><a href="#查看集群master节点" class="headerlink" title="查看集群master节点"></a>查看集群<code>master</code>节点</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl get node</span><br><span class="line">NAME      STATUS     ROLES    AGE     VERSION</span><br><span class="line">master1   NotReady   master   25m     v1.18.4</span><br><span class="line">master2   NotReady   master   12m     v1.18.4</span><br><span class="line">master3   NotReady   master   3m30s   v1.18.4</span><br></pre></td></tr></table></figure>

<p>你可以在任意一个<code>master</code>节点上执行<code>kubectl get node</code>查看集群节点的命令。</p>
<h3 id="2-7、node节点加入kubernetes集群中"><a href="#2-7、node节点加入kubernetes集群中" class="headerlink" title="2.7、node节点加入kubernetes集群中"></a>2.7、<code>node</code>节点加入<code>kubernetes</code>集群中</h3><p>正如我们上面所说的，<code>master1</code>节点初始化完成后，第二条<code>kubeadm join xxx</code>（或者说是最后一行内容）内容便是<code>node</code>节点加入集群的命令。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">~]# kubeadm join 192.168.50.100:8443 --token abcdef.0123456789abcdef \</span><br><span class="line">    --discovery-token-ca-cert-hash sha256:4c738bc8e2684c5d52d80687d48925613b66ab660403649145eb668d71d85648</span><br></pre></td></tr></table></figure>

<p>注意：<code>node</code>节点加入集群只需要执行上面的一条命令即可，只要没有报错就表示成功。不必像<code>master</code>一样做最后的加入环境变量等收尾工作。</p>
<h4 id="（1）node1节点加入集群"><a href="#（1）node1节点加入集群" class="headerlink" title="（1）node1节点加入集群"></a>（1）<code>node1</code>节点加入集群</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# kubeadm join 192.168.50.100:8443 --token abcdef.0123456789abcdef \</span><br><span class="line">&gt;     --discovery-token-ca-cert-hash sha256:4c738bc8e2684c5d52d80687d48925613b66ab660403649145eb668d71d85648</span><br><span class="line">[preflight] Running pre-flight checks</span><br><span class="line"> [WARNING IsDockerSystemdCheck]: detected &quot;cgroupfs&quot; as the Docker cgroup driver. The recommended driver is &quot;systemd&quot;. Please follow the guide at https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;setup&#x2F;cri&#x2F;</span><br><span class="line">[preflight] Reading configuration from the cluster...</span><br><span class="line">....</span><br><span class="line">....</span><br><span class="line">[kubelet-start] Writing kubelet environment file with flags to file &quot;&#x2F;var&#x2F;lib&#x2F;kubelet&#x2F;kubeadm-flags.env&quot;</span><br><span class="line">[kubelet-start] Starting the kubelet</span><br><span class="line">[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...</span><br><span class="line"></span><br><span class="line">This node has joined the cluster:</span><br><span class="line">* Certificate signing request was sent to apiserver and a response was received.</span><br><span class="line">* The Kubelet was informed of the new secure connection details.</span><br><span class="line"></span><br><span class="line">Run &#39;kubectl get nodes&#39; on the control-plane to see this node join the cluster.</span><br></pre></td></tr></table></figure>

<p>当看到倒数第四行内容<code>This node has joined the cluster</code>，这一行信息表示<code>node1</code>节点加入集群成功。</p>
<h4 id="（2）node2节点加入集群"><a href="#（2）node2节点加入集群" class="headerlink" title="（2）node2节点加入集群"></a>（2）<code>node2</code>节点加入集群</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@node2 ~]# kubeadm join 192.168.50.100:8443 --token abcdef.0123456789abcdef \</span><br><span class="line">--discovery-token-ca-cert-hash sha256:4c738bc8e2684c5d52d80687d48925613b66ab660403649145eb668d71d85648</span><br></pre></td></tr></table></figure>

<h4 id="（3）查看集群节点信息"><a href="#（3）查看集群节点信息" class="headerlink" title="（3）查看集群节点信息"></a>（3）查看集群节点信息</h4><p>此时我们可以在任意一个<code>master</code>节点执行如下命令查看此集群的节点信息。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl get nodes</span><br><span class="line">NAME      STATUS     ROLES    AGE     VERSION</span><br><span class="line">master1   NotReady   master   20h     v1.18.4</span><br><span class="line">master2   NotReady   master   20h     v1.18.4</span><br><span class="line">master3   NotReady   master   20h     v1.18.4</span><br><span class="line">node1     NotReady   &lt;none&gt;   5m15s   v1.18.4</span><br><span class="line">node2     NotReady   &lt;none&gt;   5m11s   v1.18.4</span><br></pre></td></tr></table></figure>

<p>可以看到集群的五个节点都已经存在，但是现在还不能用，也就是说现在<strong>集群节点是不可用的</strong>，原因在于上面的第2个字段，我们看到五个节点都是``NotReady`状态，这是因为我们还没有安装网络插件。</p>
<p>网络插件有<code>calico</code>，<code>flannel</code>等插件，这里我们选择使用<code>flannel</code>插件。</p>
<h3 id="2-8、安装网络插件"><a href="#2-8、安装网络插件" class="headerlink" title="2.8、安装网络插件"></a>2.8、安装网络插件</h3><h4 id="（1）默认方法"><a href="#（1）默认方法" class="headerlink" title="（1）默认方法"></a>（1）默认方法</h4><p>默认大家从网上看的教程都会使用这个命令来初始化。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~]# kubectl apply -f https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;coreos&#x2F;flannel&#x2F;master&#x2F;Documentation&#x2F;kube-flannel.yml</span><br></pre></td></tr></table></figure>

<p>事实上很多用户都不能成功，因为国内网络受限，所以可以这样子来做。</p>
<h4 id="（2）更换flannel镜像源"><a href="#（2）更换flannel镜像源" class="headerlink" title="（2）更换flannel镜像源"></a>（2）更换<code>flannel</code>镜像源</h4><p><code>master1</code>节点上修改本地的<code>hosts</code>文件添加如下内容以便解析</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">199.232.28.133  raw.githubusercontent.com</span><br></pre></td></tr></table></figure>

<p>然后下载<code>flannel</code>文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# curl -o kube-flannel.yml   https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;coreos&#x2F;flannel&#x2F;master&#x2F;Documentation&#x2F;kube-flannel.yml</span><br></pre></td></tr></table></figure>

<p>编辑镜像源，默认的镜像地址我们修改一下。把<code>yaml</code>文件中所有的<code>quay.io</code>修改为 <code>quay-mirror.qiniu.com</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# sed -i &#39;s&#x2F;quay.io&#x2F;quay-mirror.qiniu.com&#x2F;g&#39; kube-flannel.yml</span><br></pre></td></tr></table></figure>

<p>此时保存保存退出。在<code>master</code>节点执行此命令。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl apply -f kube-flannel.yml </span><br><span class="line">podsecuritypolicy.policy&#x2F;psp.flannel.unprivileged created</span><br><span class="line">clusterrole.rbac.authorization.k8s.io&#x2F;flannel created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io&#x2F;flannel created</span><br><span class="line">serviceaccount&#x2F;flannel created</span><br><span class="line">configmap&#x2F;kube-flannel-cfg created</span><br><span class="line">daemonset.apps&#x2F;kube-flannel-ds-amd64 created</span><br><span class="line">daemonset.apps&#x2F;kube-flannel-ds-arm64 created</span><br><span class="line">daemonset.apps&#x2F;kube-flannel-ds-arm created</span><br><span class="line">daemonset.apps&#x2F;kube-flannel-ds-ppc64le created</span><br><span class="line">daemonset.apps&#x2F;kube-flannel-ds-s390x created</span><br></pre></td></tr></table></figure>

<p>这样子就可以成功拉取<code>flannel</code>镜像了。当然你也可以使用我提供给大家的<code>kube-flannel.yml</code>文件</p>
<h5 id="查看flannel是否正常"><a href="#查看flannel是否正常" class="headerlink" title="查看flannel是否正常"></a>查看<code>flannel</code>是否正常</h5><p>如果你想查看<code>flannel</code>这些<code>pod</code>运行是否正常，使用如下命令</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl get pods -n kube-system | grep flannel</span><br><span class="line">NAME                              READY   STATUS    RESTARTS   AGE</span><br><span class="line">kube-flannel-ds-amd64-dp972       1&#x2F;1     Running   0          66s</span><br><span class="line">kube-flannel-ds-amd64-lkspx       1&#x2F;1     Running   0          66s</span><br><span class="line">kube-flannel-ds-amd64-rmsdk       1&#x2F;1     Running   0          66s</span><br><span class="line">kube-flannel-ds-amd64-wp668       1&#x2F;1     Running   0          66s</span><br><span class="line">kube-flannel-ds-amd64-zkrwh       1&#x2F;1     Running   0          66s</span><br></pre></td></tr></table></figure>

<p>如果第三字段<code>STATUS</code>不是处于<code>Running</code>状态的话，说明<code>flannel</code>是异常的，需要排查问题所在。</p>
<h5 id="查看节点是否为Ready"><a href="#查看节点是否为Ready" class="headerlink" title="查看节点是否为Ready"></a>查看节点是否为<code>Ready</code></h5><p>稍等片刻，执行如下指令查看节点是否可用</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl get nodes</span><br><span class="line">NAME      STATUS   ROLES    AGE   VERSION</span><br><span class="line">master1   Ready    master   21h   v1.18.4</span><br><span class="line">master2   Ready    master   21h   v1.18.4</span><br><span class="line">master3   Ready    master   21h   v1.18.4</span><br><span class="line">node1     Ready    &lt;none&gt;   62m   v1.18.4</span><br><span class="line">node2     Ready    &lt;none&gt;   62m   v1.18.4</span><br></pre></td></tr></table></figure>

<p>目前节点状态是<code>Ready</code>，表示<strong>集群节点现在是可用的</strong>。</p>
<h2 id="3、测试kubernetes集群"><a href="#3、测试kubernetes集群" class="headerlink" title="3、测试kubernetes集群"></a>3、测试<code>kubernetes</code>集群</h2><h3 id="3-1、kubernetes集群测试"><a href="#3-1、kubernetes集群测试" class="headerlink" title="3.1、kubernetes集群测试"></a>3.1、<code>kubernetes</code>集群测试</h3><h4 id="（1）创建一个nginx的pod"><a href="#（1）创建一个nginx的pod" class="headerlink" title="（1）创建一个nginx的pod"></a>（1）创建一个<code>nginx</code>的<code>pod</code></h4><p>现在我们在<code>kubernetes</code>集群中创建一个<code>nginx</code>的<code>pod</code>，验证是否能正常运行。</p>
<p>在<code>master</code>节点执行一下步骤：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl create deployment nginx --image&#x3D;nginx</span><br><span class="line">deployment.apps&#x2F;nginx created</span><br><span class="line">[root@master1 ~]# kubectl expose deployment nginx --port&#x3D;80 --type&#x3D;NodePort</span><br><span class="line">service&#x2F;nginx exposed</span><br></pre></td></tr></table></figure>

<p>现在我们查看<code>pod</code>和<code>service</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl get pod,svc -o wide</span><br></pre></td></tr></table></figure>

<p><img src="http://iubest.gitee.io/pic/640-1601003120789.webp" alt="img"></p>
<p>打印的结果中，前半部分是<code>pod</code>相关信息，后半部分是<code>service</code>相关信息。我们看<code>service/nginx</code>这一行可以看出<code>service</code>暴漏给集群的端口是<code>30249</code>。记住这个端口。</p>
<p>然后从<code>pod</code>的详细信息可以看出此时<code>pod</code>在<code>node2</code>节点之上。<code>node2</code>节点的IP地址是<code>192.168.50.132</code></p>
<h4 id="（2）访问nginx验证集群"><a href="#（2）访问nginx验证集群" class="headerlink" title="（2）访问nginx验证集群"></a>（2）访问<code>nginx</code>验证集群</h4><p>那现在我们访问一下。打开浏览器(建议火狐浏览器)，访问地址就是：<code>http://192.168.50.132:30249</code></p>
<p><img src="http://iubest.gitee.io/pic/640-1601003120792.webp" alt="img"></p>
<h3 id="3-2、安装dashboard"><a href="#3-2、安装dashboard" class="headerlink" title="3.2、安装dashboard"></a>3.2、安装<code>dashboard</code></h3><h4 id="（1）创建dashboard"><a href="#（1）创建dashboard" class="headerlink" title="（1）创建dashboard"></a>（1）创建<code>dashboard</code></h4><p>先把<code>dashboard</code>的配置文件下载下来。由于我们之前已经添加了<code>hosts</code>解析，因此可以下载。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# wget https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;kubernetes&#x2F;dashboard&#x2F;v2.0.0-beta8&#x2F;aio&#x2F;deploy&#x2F;recommended.yaml</span><br></pre></td></tr></table></figure>

<p>默认<code>Dashboard</code>只能集群内部访问，修改<code>Service</code>为<code>NodePort</code>类型，暴露到外部：</p>
<p>大概在此文件的<code>32-44</code>行之间，修改为如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">kind: Service</span><br><span class="line">apiVersion: v1</span><br><span class="line">metadata:</span><br><span class="line">  labels:</span><br><span class="line">    k8s-app: kubernetes-dashboard</span><br><span class="line">  name: kubernetes-dashboard</span><br><span class="line">  namespace: kube-system</span><br><span class="line">spec:</span><br><span class="line">  type: NodePort       #加上此行</span><br><span class="line">  ports:</span><br><span class="line">    - port: 443</span><br><span class="line">      targetPort: 8443</span><br><span class="line">      nodePort: 30001     #加上此行，端口30001可以自行定义</span><br><span class="line">  selector:</span><br><span class="line">    k8s-app: kubernetes-dashboard</span><br></pre></td></tr></table></figure>

<h4 id="运行此yaml文件"><a href="#运行此yaml文件" class="headerlink" title="运行此yaml文件"></a>运行此<code>yaml</code>文件</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl apply -f recommended.yaml </span><br><span class="line">namespace&#x2F;kubernetes-dashboard created</span><br><span class="line">serviceaccount&#x2F;kubernetes-dashboard created</span><br><span class="line">service&#x2F;kubernetes-dashboard created</span><br><span class="line">secret&#x2F;kubernetes-dashboard-certs created</span><br><span class="line">...</span><br><span class="line">service&#x2F;dashboard-metrics-scraper created</span><br><span class="line">deployment.apps&#x2F;dashboard-metrics-scraper created</span><br></pre></td></tr></table></figure>

<h5 id="查看dashboard运行是否正常"><a href="#查看dashboard运行是否正常" class="headerlink" title="查看dashboard运行是否正常"></a>查看<code>dashboard</code>运行是否正常</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl get pods -n kubernetes-dashboard</span><br><span class="line">NAME                                         READY   STATUS    RESTARTS   AGE</span><br><span class="line">dashboard-metrics-scraper-694557449d-mlnl4   1&#x2F;1     Running   0          2m31s</span><br><span class="line">kubernetes-dashboard-9774cc786-ccvcf         1&#x2F;1     Running   0          2m31s</span><br></pre></td></tr></table></figure>

<p>主要是看<code>status</code>这一列的值，如果是<code>Running</code>，并且<code>RESTARTS</code>字段的值为<code>0</code>（只要这个值不是一直在渐渐变大），就是正常的，目前来看是没有问题的。我们可以继续下一步。</p>
<p>查看此<code>dashboard</code>的<code>pod</code>运行所在的节点</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003120791.webp" alt="img"></p>
<p>从上面可以看出，<code>kubernetes-dashboard-9774cc786-ccvcf</code>运行所在的节点是<code>node2</code>上面，并且暴漏出来的端口是<code>30001</code>，所以访问地址是：<code>https://192.168.50.132:30001</code></p>
<p>用火狐浏览器访问，访问的时候会让输入<code>token</code>，从此处可以查看到<code>token</code>的值。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk &#39;&#x2F;dashboard-admin&#x2F;&#123;print $1&#125;&#39;)</span><br></pre></td></tr></table></figure>

<p><img src="http://iubest.gitee.io/pic/640-1601003120793.webp" alt="img"></p>
<p>把上面的<code>token</code>值输入进去即可进去<code>dashboard</code>界面。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003120794.webp" alt="img"></p>
<p>不过现在我们虽然可以登陆上去，但是我们权限不够还查看不了集群信息，因为我们还没有绑定集群角色，同学们可以先按照上面的尝试一下，再来做下面的步骤</p>
<h4 id="（2）cluster-admin管理员角色绑定"><a href="#（2）cluster-admin管理员角色绑定" class="headerlink" title="（2）cluster-admin管理员角色绑定"></a>（2）cluster-admin管理员角色绑定</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@master1 ~]# kubectl create serviceaccount dashboard-admin -n kube-system</span><br><span class="line">[root@master1 ~]# kubectl create clusterrolebinding dashboard-admin --clusterrole&#x3D;cluster-admin --serviceaccount&#x3D;kube-system:dashboard-admin</span><br><span class="line">[root@master1 ~]# kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk &#39;&#x2F;dashboard-admin&#x2F;&#123;print $1&#125;&#39;)</span><br></pre></td></tr></table></figure>

<p>再使用输出的<code>token</code>登陆<code>dashboard</code>即可。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003120793.webp" alt="img"></p>
<p><img src="http://iubest.gitee.io/pic/640-1601003120792.webp" alt="img"></p>
<h3 id="报错"><a href="#报错" class="headerlink" title="报错"></a>报错</h3><p>（1）其他master节点无法加入集群</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[check-etcd] Checking that the etcd cluster is healthy</span><br><span class="line">error execution phase check-etcd: error syncing endpoints with etc: context deadline exceeded</span><br><span class="line">To see the stack trace of this error execute with --v&#x3D;5 or higher</span><br></pre></td></tr></table></figure>

<p>查看集群的高可用配置是否有问题，比如keepalived的配置中，主备，优先级是否都配置好了。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/k8s/" rel="tag">k8s</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-mysql/万字总结：学习MySQL优化原理，这一篇就够了"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/mysql/%E4%B8%87%E5%AD%97%E6%80%BB%E7%BB%93%EF%BC%9A%E5%AD%A6%E4%B9%A0MySQL%E4%BC%98%E5%8C%96%E5%8E%9F%E7%90%86%EF%BC%8C%E8%BF%99%E4%B8%80%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86/"
    >万字总结：学习MySQL优化原理，这一篇就够了.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/mysql/%E4%B8%87%E5%AD%97%E6%80%BB%E7%BB%93%EF%BC%9A%E5%AD%A6%E4%B9%A0MySQL%E4%BC%98%E5%8C%96%E5%8E%9F%E7%90%86%EF%BC%8C%E8%BF%99%E4%B8%80%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/mysql/">mysql</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h2 id="万字总结：学习MySQL优化原理，这一篇就够了！"><a href="#万字总结：学习MySQL优化原理，这一篇就够了！" class="headerlink" title="万字总结：学习MySQL优化原理，这一篇就够了！"></a>万字总结：学习MySQL优化原理，这一篇就够了！</h2><p><a href="javascript:void(0);">顶级架构师</a> <em>3天前</em></p>
<p>来源 | CHEN川</p>
<p>上一篇：<a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzIzNjM3MDEyMg==&mid=2247490446&idx=1&sn=d3afb5c69392a5e18edbbbc55ab444e4&chksm=e8d9ba2bdfae333dd3caa2540618cef4177b540a049578e67d6c77f6948efe1cd07123af706b&scene=21#wechat_redirect">大公司为什么都有API网关？聊聊API网关的作用</a></p>
<p>说起MySQL的查询优化，相信大家收藏了一堆奇技淫巧：不能使用SELECT *、不使用NULL字段、合理创建索引、为字段选择合适的数据类型….. 你是否真的理解这些优化技巧？是否理解其背后的工作原理？在实际场景下性能真有提升吗？我想未必。因而理解这些优化建议背后的原理就尤为重要，希望本文能让你重新审视这些优化建议，并在实际业务场景下合理的运用。</p>
<p><strong>MySQL逻辑架构</strong></p>
<p>如果能在头脑中构建一幅MySQL各组件之间如何协同工作的架构图，有助于深入理解MySQL服务器。下图展示了MySQL的逻辑架构图。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002356113.webp" alt="img"></p>
<p>MySQL逻辑架构，来自：高性能MySQL</p>
<p>MySQL逻辑架构整体分为三层，最上层为客户端层，并非MySQL所独有，诸如：连接处理、授权认证、安全等功能均在这一层处理。</p>
<p>MySQL大多数核心服务均在中间这一层，包括查询解析、分析、优化、缓存、内置函数(比如：时间、数学、加密等函数)。所有的跨存储引擎的功能也在这一层实现：存储过程、触发器、视图等。</p>
<p>最下层为存储引擎，其负责MySQL中的数据存储和提取。和Linux下的文件系统类似，每种存储引擎都有其优势和劣势。中间的服务层通过API与存储引擎通信，这些API接口屏蔽了不同存储引擎间的差异。</p>
<p><strong>MySQL查询过程</strong></p>
<p>我们总是希望MySQL能够获得更高的查询性能，最好的办法是弄清楚MySQL是如何优化和执行查询的。一旦理解了这一点，就会发现：很多的查询优化工作实际上就是遵循一些原则让MySQL的优化器能够按照预想的合理方式运行而已。</p>
<p>当向MySQL发送一个请求的时候，MySQL到底做了些什么呢？</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002356366.webp" alt="img"></p>
<p>MySQL查询过程</p>
<p><strong>客户端/服务端通信协议</strong></p>
<p>MySQL客户端/服务端通信协议是“半双工”的：在任一时刻，要么是服务器向客户端发送数据，要么是客户端向服务器发送数据，这两个动作不能同时发生。一旦一端开始发送消息，另一端要接收完整个消息才能响应它，所以我们无法也无须将一个消息切成小块独立发送，也没有办法进行流量控制。</p>
<p>客户端用一个单独的数据包将查询请求发送给服务器，所以当查询语句很长的时候，需要设置max_allowed_packet参数。但是需要注意的是，如果查询实在是太大，服务端会拒绝接收更多数据并抛出异常。</p>
<p>与之相反的是，服务器响应给用户的数据通常会很多，由多个数据包组成。但是当服务器响应客户端请求时，客户端必须完整的接收整个返回结果，而不能简单的只取前面几条结果，然后让服务器停止发送。因而在实际开发中，尽量保持查询简单且只返回必需的数据，减小通信间数据包的大小和数量是一个非常好的习惯，这也是查询中尽量避免使用SELECT *以及加上LIMIT限制的原因之一。</p>
<p><strong>查询缓存</strong></p>
<p>在解析一个查询语句前，如果查询缓存是打开的，那么MySQL会检查这个查询语句是否命中查询缓存中的数据。如果当前查询恰好命中查询缓存，在检查一次用户权限后直接返回缓存中的结果。这种情况下，查询不会被解析，也不会生成执行计划，更不会执行。</p>
<p>MySQL将缓存存放在一个引用表（不要理解成table，可以认为是类似于HashMap的数据结构），通过一个哈希值索引，这个哈希值通过查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息计算得来。所以两个查询在任何字符上的不同（例如：空格、注释），都会导致缓存不会命中。</p>
<p>如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、MySQL库中的系统表，其查询结果都不会被缓存。比如函数NOW()或者CURRENT_DATE()会因为不同的查询时间，返回不同的查询结果，再比如包含CURRENT_USER或者CONNECION_ID()的查询语句会因为不同的用户而返回不同的结果，将这样的查询结果缓存起来没有任何的意义。</p>
<p>既然是缓存，就会失效，那查询缓存何时失效呢？MySQL的查询缓存系统会跟踪查询中涉及的每个表，如果这些表（数据或结构）发生变化，那么和这张表相关的所有缓存数据都将失效。正因为如此，在任何的写操作时，MySQL必须将对应表的所有缓存都设置为失效。如果查询缓存非常大或者碎片很多，这个操作就可能带来很大的系统消耗，甚至导致系统僵死一会儿。而且查询缓存对系统的额外消耗也不仅仅在写操作，读操作也不例外：</p>
<ol>
<li>任何的查询语句在开始之前都必须经过检查，即使这条SQL语句永远不会命中缓存</li>
<li>如果查询结果可以被缓存，那么执行完成后，会将结果存入缓存，也会带来额外的系统消耗</li>
</ol>
<p>基于此，我们要知道并不是什么情况下查询缓存都会提高系统性能，缓存和失效都会带来额外消耗，只有当缓存带来的资源节约大于其本身消耗的资源时，才会给系统带来性能提升。但要如何评估打开缓存是否能够带来性能提升是一件非常困难的事情，也不在本文讨论的范畴内。如果系统确实存在一些性能问题，可以尝试打开查询缓存，并在数据库设计上做一些优化，比如：</p>
<ol>
<li>用多个小表代替一个大表，注意不要过度设计</li>
<li>批量插入代替循环单条插入</li>
<li>合理控制缓存空间大小，一般来说其大小设置为几十兆比较合适</li>
<li>可以通过SQL_CACHE和SQL_NO_CACHE来控制某个查询语句是否需要进行缓存</li>
</ol>
<p>最后的忠告是不要轻易打开查询缓存，特别是写密集型应用。如果你实在是忍不住，可以将query_cache_type设置为DEMAND，这时只有加入SQL_CACHE的查询才会走缓存，其他查询则不会，这样可以非常自由地控制哪些查询需要被缓存。</p>
<p>当然查询缓存系统本身是非常复杂的，这里讨论的也只是很小的一部分，其他更深入的话题，比如：缓存是如何使用内存的？如何控制内存的碎片化？事务对查询缓存有何影响等等，读者可以自行阅读相关资料，这里权当抛砖引玉吧。</p>
<p><strong>语法解析和预处理</strong></p>
<p>MySQL通过关键字将SQL语句进行解析，并生成一颗对应的解析树。这个过程解析器主要通过语法规则来验证和解析。比如SQL中是否使用了错误的关键字或者关键字的顺序是否正确等等。预处理则会根据MySQL规则进一步检查解析树是否合法。比如检查要查询的数据表和数据列是否存在等。</p>
<p><strong>查询优化</strong></p>
<p>经过前面的步骤生成的语法树被认为是合法的了，并且由优化器将其转化成查询计划。多数情况下，一条查询可以有很多种执行方式，最后都返回相应的结果。优化器的作用就是找到这其中最好的执行计划。</p>
<p>MySQL使用基于成本的优化器，它尝试预测一个查询使用某种执行计划时的成本，并选择其中成本最小的一个。在MySQL可以通过查询当前会话的last_query_cost的值来得到其计算当前查询的成本。</p>
<p>mysql&gt; select * from t_message limit 10;</p>
<p>…省略结果集</p>
<p>mysql&gt; show status like ‘last_query_cost’;</p>
<p>+—————–+————-+</p>
<p>| Variable_name  | Value    |</p>
<p>+—————–+————-+</p>
<p>| Last_query_cost | 6391.799000 |</p>
<p>+—————–+————-+</p>
<p>示例中的结果表示优化器认为大概需要做6391个数据页的随机查找才能完成上面的查询。这个结果是根据一些列的统计信息计算得来的，这些统计信息包括：每张表或者索引的页面个数、索引的基数、索引和数据行的长度、索引的分布情况等等。</p>
<p>有非常多的原因会导致MySQL选择错误的执行计划，比如统计信息不准确、不会考虑不受其控制的操作成本（用户自定义函数、存储过程）、MySQL认为的最优跟我们想的不一样（我们希望执行时间尽可能短，但MySQL值选择它认为成本小的，但成本小并不意味着执行时间短）等等。</p>
<p>MySQL的查询优化器是一个非常复杂的部件，它使用了非常多的优化策略来生成一个最优的执行计划：</p>
<ul>
<li>重新定义表的关联顺序（多张表关联查询时，并不一定按照SQL中指定的顺序进行，但有一些技巧可以指定关联顺序）</li>
<li>优化MIN()和MAX()函数（找某列的最小值，如果该列有索引，只需要查找B+Tree索引最左端，反之则可以找到最大值，具体原理见下文）</li>
<li>提前终止查询（比如：使用Limit时，查找到满足数量的结果集后会立即终止查询）</li>
<li>优化排序（在老版本MySQL会使用两次传输排序，即先读取行指针和需要排序的字段在内存中对其排序，然后再根据排序结果去读取数据行，而新版本采用的是单次传输排序，也就是一次读取所有的数据行，然后根据给定的列排序。对于I/O密集型应用，效率会高很多）</li>
</ul>
<p>随着MySQL的不断发展，优化器使用的优化策略也在不断的进化，这里仅仅介绍几个非常常用且容易理解的优化策略，其他的优化策略，大家自行查阅吧。</p>
<p><strong>查询执行引擎</strong></p>
<p>在完成解析和优化阶段以后，MySQL会生成对应的执行计划，查询执行引擎根据执行计划给出的指令逐步执行得出结果。整个执行过程的大部分操作均是通过调用存储引擎实现的接口来完成，这些接口被称为handler API。查询过程中的每一张表由一个handler实例表示。实际上，MySQL在查询优化阶段就为每一张表创建了一个handler实例，优化器可以根据这些实例的接口来获取表的相关信息，包括表的所有列名、索引统计信息等。存储引擎接口提供了非常丰富的功能，但其底层仅有几十个接口，这些接口像搭积木一样完成了一次查询的大部分操作。</p>
<p><strong>返回结果给客户端</strong></p>
<p>查询执行的最后一个阶段就是将结果返回给客户端。即使查询不到数据，MySQL仍然会返回这个查询的相关信息，比如该查询影响到的行数以及执行时间等。</p>
<p>如果查询缓存被打开且这个查询可以被缓存，MySQL也会将结果存放到缓存中。</p>
<p>结果集返回客户端是一个增量且逐步返回的过程。有可能MySQL在生成第一条结果时，就开始向客户端逐步返回结果集了。这样服务端就无须存储太多结果而消耗过多内存，也可以让客户端第一时间获得返回结果。需要注意的是，结果集中的每一行都会以一个满足①中所描述的通信协议的数据包发送，再通过TCP协议进行传输，在传输过程中，可能对MySQL的数据包进行缓存然后批量发送。</p>
<p>回头总结一下MySQL整个查询执行过程，总的来说分为6个步骤：</p>
<ul>
<li>客户端向MySQL服务器发送一条查询请求</li>
<li>服务器首先检查查询缓存，如果命中缓存，则立刻返回存储在缓存中的结果。否则进入下一阶段</li>
<li>服务器进行SQL解析、预处理、再由优化器生成对应的执行计划</li>
<li>MySQL根据执行计划，调用存储引擎的API来执行查询</li>
<li>将结果返回给客户端，同时缓存查询结果</li>
</ul>
<p><strong>性能优化建议</strong></p>
<p>看了这么多，你可能会期待给出一些优化手段，是的，下面会从3个不同方面给出一些优化建议。但请等等，还有一句忠告要先送给你：<strong>不要听信你看到的关于优化的“绝对真理”，</strong>包括本文所讨论的内容，而应该是在实际的业务场景下通过测试来验证你关于执行计划以及响应时间的假设。</p>
<p><strong>Scheme设计与数据类型优化</strong></p>
<p>选择数据类型只要遵循<strong>小而简单</strong>的原则就好，越小的数据类型通常会更快，占用更少的磁盘、内存，处理时需要的CPU周期也更少。越简单的数据类型在计算时需要更少的CPU周期，比如，整型就比字符操作代价低，因而会使用整型来存储ip地址，使用DATETIME来存储时间，而不是使用字符串。</p>
<p>这里总结几个可能容易理解错误的技巧：</p>
<ol>
<li>通常来说把可为NULL的列改为NOT NULL不会对性能提升有多少帮助，只是如果计划在列上创建索引，就应该将该列设置为NOT NULL。</li>
<li>对整数类型指定宽度，比如INT(11)，没有任何卵用。INT使用32位（4个字节）存储空间，那么它的表示范围已经确定，所以INT(1)和INT(20)对于存储和计算是相同的。</li>
<li>UNSIGNED表示不允许负值，大致可以使正数的上限提高一倍。比如TINYINT存储范围是-128 ~ 127，而UNSIGNED TINYINT存储的范围却是0 - 255。</li>
<li>通常来讲，没有太大的必要使用DECIMAL数据类型。即使是在需要存储财务数据时，仍然可以使用BIGINT。比如需要精确到万分之一，那么可以将数据乘以一百万然后使用BIGINT存储。这样可以避免浮点数计算不准确和DECIMAL精确计算代价高的问题。</li>
<li>TIMESTAMP使用4个字节存储空间，DATETIME使用8个字节存储空间。因而，TIMESTAMP只能表示1970 - 2038年，比DATETIME表示的范围小得多，而且TIMESTAMP的值因时区不同而不同。</li>
<li>大多数情况下没有使用枚举类型的必要，其中一个缺点是枚举的字符串列表是固定的，添加和删除字符串（枚举选项）必须使用ALTER TABLE（如果只只是在列表末尾追加元素，不需要重建表）。</li>
<li>schema的列不要太多。原因是存储引擎的API工作时需要在服务器层和存储引擎层之间通过行缓冲格式拷贝数据，然后在服务器层将缓冲内容解码成各个列，这个转换过程的代价是非常高的。如果列太多而实际使用的列又很少的话，有可能会导致CPU占用过高。</li>
<li>大表ALTER TABLE非常耗时，MySQL执行大部分修改表结果操作的方法是用新的结构创建一个张空表，从旧表中查出所有的数据插入新表，然后再删除旧表。尤其当内存不足而表又很大，而且还有很大索引的情况下，耗时更久。当然有一些奇技淫巧可以解决这个问题，有兴趣可自行查阅。</li>
</ol>
<p><strong>创建高性能索引</strong></p>
<p>索引是提高MySQL查询性能的一个重要途径，但过多的索引可能会导致过高的磁盘使用率以及过高的内存占用，从而影响应用程序的整体性能。应当尽量避免事后才想起添加索引，因为事后可能需要监控大量的SQL才能定位到问题所在，而且添加索引的时间肯定是远大于初始添加索引所需要的时间，可见索引的添加也是非常有技术含量的。</p>
<p>搜索顶级架构师公众号回复“offer”，送你一份算法面试题和答案惊喜礼包。</p>
<p>接下来将向你展示一系列创建高性能索引的策略，以及每条策略其背后的工作原理。但在此之前，先了解与索引相关的一些算法和数据结构，将有助于更好的理解后文的内容。</p>
<p><strong>索引相关的数据结构和算法</strong></p>
<p>通常我们所说的索引是指B-Tree索引，它是目前关系型数据库中查找数据最为常用和有效的索引，大多数存储引擎都支持这种索引。使用B-Tree这个术语，是因为MySQL在CREATE TABLE或其它语句中使用了这个关键字，但实际上不同的存储引擎可能使用不同的数据结构，比如InnoDB就是使用的B+Tree。</p>
<p>B+Tree中的B是指balance，意为平衡。需要注意的是，B+树索引并不能找到一个给定键值的具体行，它找到的只是被查找数据行所在的页，接着数据库会把页读入到内存，再在内存中进行查找，最后得到要查找的数据。</p>
<p>在介绍B+Tree前，先了解一下二叉查找树，它是一种经典的数据结构，其左子树的值总是小于根的值，右子树的值总是大于根的值，如下图①。如果要在这课树中查找值为5的记录，其大致流程：先找到根，其值为6，大于5，所以查找左子树，找到3，而5大于3，接着找3的右子树，总共找了3次。同样的方法，如果查找值为8的记录，也需要查找3次。所以二叉查找树的平均查找次数为(3 + 3 + 3 + 2 + 2 + 1) / 6 = 2.3次，而顺序查找的话，查找值为2的记录，仅需要1次，但查找值为8的记录则需要6次，所以顺序查找的平均查找次数为：(1 + 2 + 3 + 4 + 5 + 6) / 6 = 3.3次，因此大多数情况下二叉查找树的平均查找速度比顺序查找要快。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002356328.webp" alt="img"></p>
<p>二叉查找树和平衡二叉树</p>
<p>由于二叉查找树可以任意构造，同样的值，可以构造出如图②的二叉查找树，显然这棵二叉树的查询效率和顺序查找差不多。若想二叉查找数的查询性能最高，需要这棵二叉查找树是平衡的，也即平衡二叉树（AVL树）。</p>
<p>平衡二叉树首先需要符合二叉查找树的定义，其次必须满足任何节点的两个子树的高度差不能大于1。显然图②不满足平衡二叉树的定义，而图①是一课平衡二叉树。平衡二叉树的查找性能是比较高的（性能最好的是最优二叉树），查询性能越好，维护的成本就越大。比如图①的平衡二叉树，当用户需要插入一个新的值9的节点时，就需要做出如下变动。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002356327.webp" alt="img"></p>
<p>平衡二叉树旋转</p>
<p>通过一次左旋操作就将插入后的树重新变为平衡二叉树是最简单的情况了，实际应用场景中可能需要旋转多次。至此我们可以考虑一个问题，平衡二叉树的查找效率还不错，实现也非常简单，相应的维护成本还能接受，为什么MySQL索引不直接使用平衡二叉树？</p>
<p>随着数据库中数据的增加，索引本身大小随之增加，不可能全部存储在内存中，因此索引往往以索引文件的形式存储的磁盘上。这样的话，索引查找过程中就要产生磁盘I/O消耗，相对于内存存取，I/O存取的消耗要高几个数量级。可以想象一下一棵几百万节点的二叉树的深度是多少？如果将这么大深度的一颗二叉树放磁盘上，每读取一个节点，需要一次磁盘的I/O读取，整个查找的耗时显然是不能够接受的。那么如何减少查找过程中的I/O存取次数？</p>
<p>一种行之有效的解决方法是减少树的深度，将二叉树变为m叉树（多路搜索树），而B+Tree就是一种多路搜索树。理解B+Tree时，只需要理解其最重要的两个特征即可：第一，所有的关键字（可以理解为数据）都存储在叶子节点（Leaf Page），非叶子节点（Index Page）并不存储真正的数据，所有记录节点都是按键值大小顺序存放在同一层叶子节点上。其次，所有的叶子节点由指针连接。如下图为高度为2的简化了的B+Tree。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002356329.webp" alt="img"></p>
<p>简化B+Tree</p>
<p>怎么理解这两个特征？MySQL将每个节点的大小设置为一个页的整数倍（原因下文会介绍），也就是在节点空间大小一定的情况下，每个节点可以存储更多的内结点，这样每个结点能索引的范围更大更精确。所有的叶子节点使用指针链接的好处是可以进行区间访问，比如上图中，如果查找大于20而小于30的记录，只需要找到节点20，就可以遍历指针依次找到25、30。如果没有链接指针的话，就无法进行区间查找。这也是MySQL使用B+Tree作为索引存储结构的重要原因。</p>
<p>MySQL为何将节点大小设置为页的整数倍，这就需要理解磁盘的存储原理。磁盘本身存取就比主存慢很多，在加上机械运动损耗（特别是普通的机械硬盘），磁盘的存取速度往往是主存的几百万分之一，为了尽量减少磁盘I/O，磁盘往往不是严格按需读取，而是每次都会预读，即使只需要一个字节，磁盘也会从这个位置开始，顺序向后读取一定长度的数据放入内存，预读的长度一般为页的整数倍。</p>
<p>页是计算机管理存储器的逻辑块，硬件及OS往往将主存和磁盘存储区分割为连续的大小相等的块，每个存储块称为一页（许多OS中，页的大小通常为4K）。主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时，会触发一个缺页异常，此时系统会向磁盘发出读盘信号，磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中，然后一起返回，程序继续运行。</p>
<p>MySQL巧妙利用了磁盘预读原理，将一个节点的大小设为等于一个页，这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的，每次新建节点时，直接申请一个页的空间，这样就保证一个节点物理上也存储在一个页里，加之计算机存储分配都是按页对齐的，就实现了读取一个节点只需一次I/O。假设B+Tree的高度为h，一次检索最多需要h-1I/O（根节点常驻内存），复杂度$O(h) = O(\log_{M}N)$。实际应用场景中，M通常较大，常常超过100，因此树的高度一般都比较小，通常不超过3。</p>
<p>最后简单了解下B+Tree节点的操作，在整体上对索引的维护有一个大概的了解，虽然索引可以大大提高查询效率，但维护索引仍要花费很大的代价，因此合理的创建索引也就尤为重要。</p>
<p>仍以上面的树为例，我们假设每个节点只能存储4个内节点。首先要插入第一个节点28，如下图所示。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002356357.webp" alt="img"></p>
<p>leaf page和index page都没有满</p>
<p>接着插入下一个节点70，在Index Page中查询后得知应该插入到50 - 70之间的叶子节点，但叶子节点已满，这时候就需要进行也分裂的操作，当前的叶子节点起点为50，所以根据中间值来拆分叶子节点，如下图所示。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002356362.webp" alt="img"></p>
<p>Leaf Page拆分</p>
<p>最后插入一个节点95，这时候Index Page和Leaf Page都满了，就需要做两次拆分，如下图所示。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002355978.webp" alt="img"></p>
<p>Leaf Page与Index Page拆分</p>
<p>拆分后最终形成了这样一颗树。</p>
<p>搜索顶级架构师公众号回复“架构整洁”，送你一份惊喜礼包。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002356356.webp" alt="img"></p>
<p>最终树</p>
<p>B+Tree为了保持平衡，对于新插入的值需要做大量的拆分页操作，而页的拆分需要I/O操作，为了尽可能的减少页的拆分操作，B+Tree也提供了类似于平衡二叉树的旋转功能。当Leaf Page已满但其左右兄弟节点没有满的情况下，B+Tree并不急于去做拆分操作，而是将记录移到当前所在页的兄弟节点上。通常情况下，左兄弟会被先检查用来做旋转操作。就比如上面第二个示例，当插入70的时候，并不会去做页拆分，而是左旋操作。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002356267.webp" alt="img"></p>
<p>左旋操作</p>
<p>通过旋转操作可以最大限度的减少页分裂，从而减少索引维护过程中的磁盘的I/O操作，也提高索引维护效率。需要注意的是，删除节点跟插入节点类似，仍然需要旋转和拆分操作，这里就不再说明。</p>
<p><strong>高性能策略</strong></p>
<p>通过上文，相信你对B+Tree的数据结构已经有了大致的了解，但MySQL中索引是如何组织数据的存储呢？以一个简单的示例来说明，假如有如下数据表：</p>
<p>CREATE TABLE People(</p>
<p>  last_name varchar(50) not null,</p>
<p>  first_name varchar(50) not null,</p>
<p>  dob date not null,</p>
<p>  gender enum(<code>m</code>,<code>f</code>) not null,</p>
<p>  key(last_name,first_name,dob)</p>
<p>);</p>
<p>对于表中每一行数据，索引中包含了last_name、first_name、dob列的值，下图展示了索引是如何组织数据存储的。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601002356312.webp" alt="img"></p>
<p>索引如何组织数据存储，来自：高性能MySQL</p>
<p>可以看到，索引首先根据第一个字段来排列顺序，当名字相同时，则根据第三个字段，即出生日期来排序，正是因为这个原因，才有了索引的“最左原则”。</p>
<p><strong>1、MySQL不会使用索引的情况：非独立的列</strong></p>
<p>“独立的列”是指索引列不能是表达式的一部分，也不能是函数的参数。比如：</p>
<p>select * from where id + 1 = 5</p>
<p>我们很容易看出其等价于 id = 4，但是MySQL无法自动解析这个表达式，使用函数是同样的道理。</p>
<p><strong>2、前缀索引</strong></p>
<p>如果列很长，通常可以索引开始的部分字符，这样可以有效节约索引空间，从而提高索引效率。</p>
<p><strong>3、多列索引和索引顺序</strong></p>
<p>在多数情况下，在多个列上建立独立的索引并不能提高查询性能。理由非常简单，MySQL不知道选择哪个索引的查询效率更好，所以在老版本，比如MySQL5.0之前就会随便选择一个列的索引，而新的版本会采用合并索引的策略。举个简单的例子，在一张电影演员表中，在actor_id和film_id两个列上都建立了独立的索引，然后有如下查询：</p>
<p>select film_id,actor_id from film_actor where actor_id = 1 or film_id = 1</p>
<p>老版本的MySQL会随机选择一个索引，但新版本做如下的优化：</p>
<p>select film_id,actor_id from film_actor where actor_id = 1 </p>
<p>union all</p>
<p>select film_id,actor_id from film_actor where film_id = 1 and actor_id &lt;&gt; 1</p>
<ul>
<li>当出现多个索引做相交操作时（多个AND条件），通常来说一个包含所有相关列的索引要优于多个独立索引。</li>
<li>当出现多个索引做联合操作时（多个OR条件），对结果集的合并、排序等操作需要耗费大量的CPU和内存资源，特别是当其中的某些索引的选择性不高，需要返回合并大量数据时，查询成本更高。所以这种情况下还不如走全表扫描。</li>
</ul>
<p>因此explain时如果发现有索引合并（Extra字段出现Using union），应该好好检查一下查询和表结构是不是已经是最优的，如果查询和表都没有问题，那只能说明索引建的非常糟糕，应当慎重考虑索引是否合适，有可能一个包含所有相关列的多列索引更适合。</p>
<p>前面我们提到过索引如何组织数据存储的，从图中可以看到多列索引时，索引的顺序对于查询是至关重要的，很明显应该把选择性更高的字段放到索引的前面，这样通过第一个字段就可以过滤掉大多数不符合条件的数据。</p>
<p>索引选择性是指不重复的索引值和数据表的总记录数的比值，选择性越高查询效率越高，因为选择性越高的索引可以让MySQL在查询时过滤掉更多的行。唯一索引的选择性是1，这时最好的索引选择性，性能也是最好的。</p>
<p>理解索引选择性的概念后，就不难确定哪个字段的选择性较高了，查一下就知道了，比如：</p>
<p>SELECT * FROM payment where staff_id = 2 and customer_id = 584</p>
<p>是应该创建(staff_id,customer_id)的索引还是应该颠倒一下顺序？执行下面的查询，哪个字段的选择性更接近1就把哪个字段索引前面就好。</p>
<p>select count(distinct staff_id)/count(*) as staff_id_selectivity,</p>
<p>​    count(distinct customer_id)/count(*) as customer_id_selectivity,</p>
<p>​    count(*) from payment</p>
<p>多数情况下使用这个原则没有任何问题，但仍然注意你的数据中是否存在一些特殊情况。举个简单的例子，比如要查询某个用户组下有过交易的用户信息：</p>
<p>select user_id from trade where user_group_id = 1 and trade_amount &gt; 0</p>
<p>MySQL为这个查询选择了索引(user_group_id,trade_amount)，如果不考虑特殊情况，这看起来没有任何问题，但实际情况是这张表的大多数数据都是从老系统中迁移过来的，由于新老系统的数据不兼容，所以就给老系统迁移过来的数据赋予了一个默认的用户组。这种情况下，通过索引扫描的行数跟全表扫描基本没什么区别，索引也就起不到任何作用。</p>
<p>推广开来说，经验法则和推论在多数情况下是有用的，可以指导我们开发和设计，但实际情况往往会更复杂，实际业务场景下的某些特殊情况可能会摧毁你的整个设计。</p>
<p><strong>4、避免多个范围条件</strong></p>
<p>实际开发中，我们会经常使用多个范围条件，比如想查询某个时间段内登录过的用户：</p>
<p>select user.* from user where login_time &gt; ‘2017-04-01’ and age between 18 and 30;</p>
<p>这个查询有一个问题：它有两个范围条件，login_time列和age列，MySQL可以使用login_time列的索引或者age列的索引，但无法同时使用它们。</p>
<p><strong>5、覆盖索引</strong></p>
<p>如果一个索引包含或者说覆盖所有需要查询的字段的值，那么就没有必要再回表查询，这就称为覆盖索引。覆盖索引是非常有用的工具，可以极大的提高性能，因为查询只需要扫描索引会带来许多好处：</p>
<ul>
<li>索引条目远小于数据行大小，如果只读取索引，极大减少数据访问量</li>
<li>索引是有按照列值顺序存储的，对于I/O密集型的范围查询要比随机从磁盘读取每一行数据的IO要少的多</li>
</ul>
<p><strong>6、使用索引扫描来排序</strong></p>
<p>MySQL有两种方式可以生产有序的结果集，其一是对结果集进行排序的操作，其二是按照索引顺序扫描得出的结果自然是有序的。如果explain的结果中type列的值为index表示使用了索引扫描来做排序。</p>
<p>扫描索引本身很快，因为只需要从一条索引记录移动到相邻的下一条记录。但如果索引本身不能覆盖所有需要查询的列，那么就不得不每扫描一条索引记录就回表查询一次对应的行。这个读取操作基本上是随机I/O，因此按照索引顺序读取数据的速度通常要比顺序地全表扫描要慢。</p>
<p>在设计索引时，如果一个索引既能够满足排序，又满足查询，是最好的。</p>
<p>搜索程序员小乐公众号回复“Java”，送你一份Java面试题和答案惊喜礼包。</p>
<p>只有当索引的列顺序和ORDER BY子句的顺序完全一致，并且所有列的排序方向也一样时，才能够使用索引来对结果做排序。如果查询需要关联多张表，则只有ORDER BY子句引用的字段全部为第一张表时，才能使用索引做排序。ORDER BY子句和查询的限制是一样的，都要满足最左前缀的要求（有一种情况例外，就是最左的列被指定为常数，下面是一个简单的示例），其它情况下都需要执行排序操作，而无法利用索引排序。</p>
<p>// 最左列为常数，索引：(date,staff_id,customer_id)</p>
<p>select staff_id,customer_id from demo where date = ‘2015-06-01’ order by staff_id,customer_id</p>
<p><strong>7、冗余和重复索引</strong></p>
<p>冗余索引是指在相同的列上按照相同的顺序创建的相同类型的索引，应当尽量避免这种索引，发现后立即删除。比如有一个索引(A,B)，再创建索引(A)就是冗余索引。冗余索引经常发生在为表添加新索引时，比如有人新建了索引(A,B)，但这个索引不是扩展已有的索引(A)。</p>
<p>大多数情况下都应该尽量扩展已有的索引而不是创建新索引。但有极少情况下出现性能方面的考虑需要冗余索引，比如扩展已有索引而导致其变得过大，从而影响到其他使用该索引的查询。</p>
<p><strong>8、删除长期未使用的索引</strong></p>
<p>定期删除一些长时间未使用过的索引是一个非常好的习惯。</p>
<p>关于索引这个话题打算就此打住，最后要说一句，索引并不总是最好的工具，只有当索引帮助提高查询速度带来的好处大于其带来的额外工作时，索引才是有效的。对于非常小的表，简单的全表扫描更高效。对于中到大型的表，索引就非常有效。对于超大型的表，建立和维护索引的代价随之增长，这时候其他技术也许更有效，比如分区表。最后的最后，explain后再提测是一种美德。</p>
<p><strong>优化COUNT()查询</strong></p>
<p>COUNT()可能是被大家误解最多的函数了，它有两种不同的作用，其一是统计某个列值的数量，其二是统计行数。统计列值时，要求列值是非空的，它不会统计NULL。如果确认括号中的表达式不可能为空时，实际上就是在统计行数。最简单的就是当使用COUNT(*)时，并不是我们所想象的那样扩展成所有的列，实际上，它会忽略所有的列而直接统计所有的行数。</p>
<p>我们最常见的误解也就在这儿，在括号内指定了一列却希望统计结果是行数，而且还常常误以为前者的性能会更好。但实际并非这样，如果要统计行数，直接使用COUNT(*)，意义清晰，且性能更好。</p>
<p>有时候某些业务场景并不需要完全精确的COUNT值，可以用近似值来代替，EXPLAIN出来的行数就是一个不错的近似值，而且执行EXPLAIN并不需要真正地去执行查询，所以成本非常低。通常来说，执行COUNT()都需要扫描大量的行才能获取到精确的数据，因此很难优化，MySQL层面还能做得也就只有覆盖索引了。如果不还能解决问题，只有从架构层面解决了，比如添加汇总表，或者使用redis这样的外部缓存系统。</p>
<p><strong>优化关联查询</strong></p>
<p>在大数据场景下，表与表之间通过一个冗余字段来关联，要比直接使用JOIN有更好的性能。如果确实需要使用关联查询的情况下，需要特别注意的是：</p>
<ol>
<li>确保ON和USING字句中的列上有索引。在创建索引的时候就要考虑到关联的顺序。当表A和表B用列c关联的时候，如果优化器关联的顺序是A、B，那么就不需要在A表的对应列上创建索引。没有用到的索引会带来额外的负担，一般来说，除非有其他理由，只需要在关联顺序中的第二张表的相应列上创建索引（具体原因下文分析）。</li>
<li>确保任何的GROUP BY和ORDER BY中的表达式只涉及到一个表中的列，这样MySQL才有可能使用索引来优化。</li>
</ol>
<p>要理解优化关联查询的第一个技巧，就需要理解MySQL是如何执行关联查询的。当前MySQL关联执行的策略非常简单，它对任何的关联都执行<strong>嵌套循环关联操作</strong>，即先在一个表中循环取出单条数据，然后在嵌套循环到下一个表中寻找匹配的行，依次下去，直到找到所有表中匹配的行为为止。然后根据各个表匹配的行，返回查询中需要的各个列。</p>
<p>太抽象了？以上面的示例来说明，比如有这样的一个查询：</p>
<p>SELECT A.xx,B.yy</p>
<p>FROM A INNER JOIN B USING(c)</p>
<p>WHERE A.xx IN (5,6)</p>
<p>假设MySQL按照查询中的关联顺序A、B来进行关联操作，那么可以用下面的伪代码表示MySQL如何完成这个查询：</p>
<p>outer_iterator = SELECT A.xx,A.c FROM A WHERE A.xx IN (5,6);</p>
<p>outer_row = outer_iterator.next;</p>
<p>while(outer_row) {</p>
<p>  inner_iterator = SELECT B.yy FROM B WHERE B.c = outer_row.c;</p>
<p>  inner_row = inner_iterator.next;</p>
<p>  while(inner_row) {</p>
<p>​    output[inner_row.yy,outer_row.xx];</p>
<p>​    inner_row = inner_iterator.next;</p>
<p>  }</p>
<p>  outer_row = outer_iterator.next;</p>
<p>}</p>
<p>可以看到，最外层的查询是根据A.xx列来查询的，A.c上如果有索引的话，整个关联查询也不会使用。再看内层的查询，很明显B.c上如果有索引的话，能够加速查询，因此只需要在关联顺序中的第二张表的相应列上创建索引即可。</p>
<p><strong>优化LIMIT分页</strong></p>
<p>当需要分页操作时，通常会使用LIMIT加上偏移量的办法实现，同时加上合适的ORDER BY字句。如果有对应的索引，通常效率会不错，否则，MySQL需要做大量的文件排序操作。</p>
<p>一个常见的问题是当偏移量非常大的时候，比如：LIMIT 10000 20这样的查询，MySQL需要查询10020条记录然后只返回20条记录，前面的10000条都将被抛弃，这样的代价非常高。</p>
<p>优化这种查询一个最简单的办法就是尽可能的使用覆盖索引扫描，而不是查询所有的列。然后根据需要做一次关联查询再返回所有的列。对于偏移量很大时，这样做的效率会提升非常大。考虑下面的查询：</p>
<p>SELECT film_id,description FROM film ORDER BY title LIMIT 50,5;</p>
<p>如果这张表非常大，那么这个查询最好改成下面的样子：</p>
<p>SELECT film.film_id,film.description</p>
<p>FROM film INNER JOIN (</p>
<p>  SELECT film_id FROM film ORDER BY title LIMIT 50,5</p>
<p>) AS tmp USING(film_id);</p>
<p>这里的延迟关联将大大提升查询效率，让MySQL扫描尽可能少的页面，获取需要访问的记录后在根据关联列回原表查询所需要的列。</p>
<p>有时候如果可以使用书签记录上次取数据的位置，那么下次就可以直接从该书签记录的位置开始扫描，这样就可以避免使用OFFSET，比如下面的查询：</p>
<p>SELECT id FROM t LIMIT 10000, 10;</p>
<p>改为：</p>
<p>SELECT id FROM t WHERE id &gt; 10000 LIMIT 10;</p>
<p>其它优化的办法还包括使用预先计算的汇总表，或者关联到一个冗余表，冗余表中只包含主键列和需要做排序的列。</p>
<p><strong>优化UNION</strong></p>
<p>MySQL处理UNION的策略是先创建临时表，然后再把各个查询结果插入到临时表中，最后再来做查询。因此很多优化策略在UNION查询中都没有办法很好的时候。经常需要手动将WHERE、LIMIT、ORDER BY等字句“下推”到各个子查询中，以便优化器可以充分利用这些条件先优化。</p>
<p>除非确实需要服务器去重，否则就一定要使用UNION ALL，如果没有ALL关键字，MySQL会给临时表加上DISTINCT选项，这会导致整个临时表的数据做唯一性检查，这样做的代价非常高。当然即使使用ALL关键字，MySQL总是将结果放入临时表，然后再读出，再返回给客户端。虽然很多时候没有这个必要，比如有时候可以直接把每个子查询的结果返回给客户端。</p>
<p><strong>结语</strong></p>
<p>理解查询是如何执行以及时间都消耗在哪些地方，再加上一些优化过程的知识，可以帮助大家更好的理解MySQL，理解常见优化技巧背后的原理。希望本文中的原理、示例能够帮助大家更好的将理论和实践联系起来，更多的将理论知识运用到实践中。</p>
<p>其他也没啥说的了，给大家留两个思考题吧，可以在脑袋里想想答案，这也是大家经常挂在嘴边的，但很少有人会思考为什么？</p>
<ol>
<li>有非常多的程序员在分享时都会抛出这样一个观点：尽可能不要使用存储过程，存储过程非常不容易维护，也会增加使用成本，应该把业务逻辑放到客户端。既然客户端都能干这些事，那为什么还要存储过程？</li>
<li>JOIN本身也挺方便的，直接查询就好了，为什么还需要视图呢？</li>
</ol>
<p><strong>参考资料</strong></p>
<ol>
<li><p>姜承尧 著；MySQL技术内幕-InnoDB存储引擎；机械工业出版社，2013</p>
</li>
<li><p>Baron Scbwartz 等著；宁海元 周振兴等译；高性能MySQL（第三版）; 电子工业出版社， 2013</p>
</li>
<li><p>由 B-/B+树看 MySQL索引结构</p>
<p><a target="_blank" rel="noopener" href="https://segmentfault.com/a/1190000004690721">https://segmentfault.com/a/1190000004690721</a></p>
</li>
</ol>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/mysql/" rel="tag">mysql</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-tips/Git 从入门到精通，这篇包教包会"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/tips/Git%20%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A%EF%BC%8C%E8%BF%99%E7%AF%87%E5%8C%85%E6%95%99%E5%8C%85%E4%BC%9A/"
    >Git 从入门到精通，这篇包教包会.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/tips/Git%20%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A%EF%BC%8C%E8%BF%99%E7%AF%87%E5%8C%85%E6%95%99%E5%8C%85%E4%BC%9A/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/tips/">tips</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="Git-从入门到精通，这篇包教包会"><a href="#Git-从入门到精通，这篇包教包会" class="headerlink" title="Git 从入门到精通，这篇包教包会"></a>Git 从入门到精通，这篇包教包会</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><h3 id="Git-是什么？"><a href="#Git-是什么？" class="headerlink" title="Git 是什么？"></a>Git 是什么？</h3><p>Git 是一个开源的分布式版本控制系统。</p>
<h3 id="什么是版本控制？"><a href="#什么是版本控制？" class="headerlink" title="什么是版本控制？"></a>什么是版本控制？</h3><p>版本控制是一种记录一个或若干文件内容变化，以便将来查阅特定版本修订情况的系统。</p>
<h3 id="什么是分布式版本控制系统？"><a href="#什么是分布式版本控制系统？" class="headerlink" title="什么是分布式版本控制系统？"></a>什么是分布式版本控制系统？</h3><p>介绍分布式版本控制系统前，有必要先了解一下传统的集中式版本控制系统。</p>
<p><strong>集中化的版本控制系统</strong>，诸如 CVS，Subversion 等，都有一个单一的集中管理的服务器，保存所有文件的修订版本，而协同工作的人们都通过客户端连到这台服务器，取出最新的文件或者提交更新。</p>
<p>这么做最显而易见的缺点是中央服务器的单点故障。如果宕机一小时，那么在这一小时内，谁都无法提交更新，也就无法协同工作。要是中央服务器的磁盘发生故障，碰巧没做备份，或者备份不够及时，就会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601170309822.webp" alt="img"></p>
<p><strong>分布式版本控制系统</strong>的客户端并不只提取最新版本的文件快照，而是把代码仓库完整地镜像下来。这么一来，任何一处协同工作用的服务器发生故障，事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的提取操作，实际上都是一次对代码仓库的完整备份。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601170309827.webp" alt="img"></p>
<h3 id="为什么使用-Git？"><a href="#为什么使用-Git？" class="headerlink" title="为什么使用 Git？"></a>为什么使用 Git？</h3><p>Git 是分布式的。这是 Git 和其它非分布式的版本控制系统，例如 svn，cvs 等，最核心的区别。分布式带来以下好处：</p>
<p><strong>工作时不需要联网</strong></p>
<p>首先，分布式版本控制系统根本没有 “中央服务器”，每个人的电脑上都是一个完整的版本库，这样，你工作的时候，就不需要联网了，因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库，那多个人如何协作呢？比方说你在自己电脑上改了文件 A，你的同事也在他的电脑上改了文件 A，这时，你们俩之间只需把各自的修改推送给对方，就可以互相看到对方的修改了。</p>
<p><strong>更加安全</strong></p>
<p>集中式版本控制系统，一旦中央服务器出了问题，所有人都无法工作。</p>
<p>分布式版本控制系统，每个人电脑中都有完整的版本库，所以某人的机器挂了，并不影响其它人。</p>
<h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p><strong>Debian/Ubuntu 环境安装</strong></p>
<p>如果你使用的系统是 Debian/Ubuntu ， 安装命令为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \</span><br><span class="line">&gt; libz-dev libssl-dev</span><br><span class="line">$ apt-get install git-core</span><br><span class="line">$ git --version</span><br><span class="line">git version 1.8.1.2</span><br></pre></td></tr></table></figure>

<p><strong>Centos/RedHat 环境安装</strong></p>
<p>如果你使用的系统是 Centos/RedHat ，安装命令为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ yum install curl-devel expat-devel gettext-devel \</span><br><span class="line">&gt; openssl-devel zlib-devel</span><br><span class="line">$ yum -y install git-core</span><br><span class="line">$ git --version</span><br><span class="line">git version 1.7.1</span><br></pre></td></tr></table></figure>

<p><strong>Windows 环境安装</strong></p>
<p>在 Git 官方下载地址下载 exe 安装包。按照安装向导安装即可。</p>
<p>建议安装 Git Bash 这个 git 的命令行工具。</p>
<p><strong>Mac 环境安装</strong></p>
<p>在 Git 官方下载地址下载 mac 安装包。按照安装向导安装即可。</p>
<h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>Git 自带一个 <code>git config</code> 的工具来帮助设置控制 Git 外观和行为的配置变量。这些变量存储在三个不同的位置：</p>
<ol>
<li><code>/etc/gitconfig</code> 文件: 包含系统上每一个用户及他们仓库的通用配置。如果使用带有 <code>--system</code> 选项的 <code>git config</code> 时，它会从此文件读写配置变量。</li>
<li><code>\~/.gitconfig</code> 或 <code>\~/.config/git/config</code> 文件：只针对当前用户。可以传递 <code>--global</code> 选项让 Git 读写此文件。</li>
<li>当前使用仓库的 Git 目录中的 <code>config</code> 文件（就是 <code>.git/config</code>）：针对该仓库。</li>
</ol>
<p>每一个级别覆盖上一级别的配置，所以 <code>.git/config</code> 的配置变量会覆盖 <code>/etc/gitconfig</code> 中的配置变量。</p>
<p>在 Windows 系统中，Git 会查找 <code>$HOME</code> 目录下（一般情况下是 <code>C:\Users\$USER</code>）的 <code>.gitconfig</code> 文件。Git 同样也会寻找 <code>/etc/gitconfig</code> 文件，但只限于 MSys 的根目录下，即安装 Git 时所选的目标位置。</p>
<h3 id="用户信息"><a href="#用户信息" class="headerlink" title="用户信息"></a>用户信息</h3><p>当安装完 Git 应该做的第一件事就是设置你的用户名称与邮件地址。这样做很重要，因为每一个 Git 的提交都会使用这些信息，并且它会写入到你的每一次提交中，不可更改：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git config --global user.name &quot;John Doe&quot;</span><br><span class="line">$ git config --global user.email johndoe@example.com</span><br></pre></td></tr></table></figure>

<p>再次强调，如果使用了 <code>--global</code> 选项，那么该命令只需要运行一次，因为之后无论你在该系统上做任何事情， Git 都会使用那些信息。当你想针对特定项目使用不同的用户名称与邮件地址时，可以在那个项目目录下运行没有 <code>--global</code> 选项的命令来配置。</p>
<p>很多 GUI 工具都会在第一次运行时帮助你配置这些信息。</p>
<h3 id="gitignore"><a href="#gitignore" class="headerlink" title=".gitignore"></a>.gitignore</h3><p><code>.gitignore</code> 文件可能从字面含义也不难猜出：这个文件里配置的文件或目录，会自动被 git 所忽略，不纳入版本控制。</p>
<p>在日常开发中，我们的项目经常会产生一些临时文件，如编译 Java 产生的 <code>*.class</code> 文件，又或是 IDE 自动生成的隐藏目录（Intellij 的 <code>.idea</code> 目录、Eclipse 的 <code>.settings</code> 目录等）等等。这些文件或目录实在没必要纳入版本管理。在这种场景下，你就需要用到 <code>.gitignore</code> 配置来过滤这些文件或目录。</p>
<p>配置的规则很简单，也没什么可说的，看几个例子，自然就明白了。</p>
<p>这里推荐一下 Github 的开源项目：<a target="_blank" rel="noopener" href="https://github.com/github/gitignore">https://github.com/github/gitignore</a></p>
<p>在这里，你可以找到很多常用的模板，如：Java、Nodejs、C++ 的 <code>.gitignore</code> 模板等等。</p>
<h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><p>个人认为，对于 Git 这个版本工具，再不了解原理的情况下，直接去学习命令行，可能会一头雾水。所以，本文特意将原理放在命令使用章节之前讲解。</p>
<h3 id="版本库"><a href="#版本库" class="headerlink" title="版本库"></a>版本库</h3><p>当你一个项目到本地或创建一个 git 项目，项目目录下会有一个隐藏的 <code>.git</code> 子目录。这个目录是 git 用来跟踪管理版本库的，千万不要手动修改。</p>
<h3 id="哈希值"><a href="#哈希值" class="headerlink" title="哈希值"></a>哈希值</h3><p>Git 中所有数据在存储前都计算校验和，然后以校验和来引用。这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。这个功能建构在 Git 底层，是构成 Git 哲学不可或缺的部分。若你在传送过程中丢失信息或损坏文件，Git 就能发现。</p>
<p>Git 用以计算校验和的机制叫做 SHA-1 散列（hash，哈希）。这是一个由 40 个十六进制字符（0-9 和 a-f）组成字符串，基于 Git 中文件的内容或目录结构计算出来。SHA-1 哈希看起来是这样：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">24b9da6552252987aa493b52f8696cd6d3b00373</span><br></pre></td></tr></table></figure>

<p>Git 中使用这种哈希值的情况很多，你将经常看到这种哈希值。实际上，Git 数据库中保存的信息都是以文件内容的哈希值来索引，而不是文件名。</p>
<h3 id="文件状态"><a href="#文件状态" class="headerlink" title="文件状态"></a>文件状态</h3><p>在 GIt 中，你的文件可能会处于三种状态之一：</p>
<ul>
<li><strong>已修改（modified）</strong> - 已修改表示修改了文件，但还没保存到数据库中。</li>
<li><strong>已暂存（staged）</strong> - 已暂存表示对一个已修改文件的当前版本做了标记，使之包含在下次提交的快照中。</li>
<li><strong>已提交（committed）</strong> - 已提交表示数据已经安全的保存在本地数据库中。</li>
</ul>
<h3 id="工作区域"><a href="#工作区域" class="headerlink" title="工作区域"></a>工作区域</h3><p>与文件状态对应的，不同状态的文件在 Git 中处于不同的工作区域。</p>
<ul>
<li><strong>工作区（working）</strong> - 当你 <code>git clone</code> 一个项目到本地，相当于在本地克隆了项目的一个副本。工作区是对项目的某个版本独立提取出来的内容。这些从 Git 仓库的压缩数据库中提取出来的文件，放在磁盘上供你使用或修改。</li>
<li><strong>暂存区（staging）</strong> - 暂存区是一个文件，保存了下次将提交的文件列表信息，一般在 Git 仓库目录中。有时候也被称作 `‘索引’’，不过一般说法还是叫暂存区。</li>
<li><strong>本地仓库（local）</strong> - 提交更新，找到暂存区域的文件，将快照永久性存储到 Git 本地仓库。</li>
<li><strong>远程仓库（remote）</strong> - 以上几个工作区都是在本地。为了让别人可以看到你的修改，你需要将你的更新推送到远程仓库。同理，如果你想同步别人的修改，你需要从远程仓库拉取更新。</li>
</ul>
<p><img src="http://iubest.gitee.io/pic/640-1601170309691.webp" alt="img"></p>
<h2 id="命令"><a href="#命令" class="headerlink" title="命令"></a>命令</h2><p>国外网友制作了一张 Git Cheat Sheet，总结很精炼，各位不妨收藏一下。</p>
<p>本节选择性介绍 git 中比较常用的命令行场景。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601170309676.webp" alt="img"></p>
<h3 id="创建仓库"><a href="#创建仓库" class="headerlink" title="创建仓库"></a>创建仓库</h3><p>克隆一个已创建的仓库：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># 通过 SSH</span><br><span class="line">$ git clone ssh:&#x2F;&#x2F;user@domain.com&#x2F;repo.git</span><br><span class="line"></span><br><span class="line">#通过 HTTP</span><br><span class="line">$ git clone http:&#x2F;&#x2F;domain.com&#x2F;user&#x2F;repo.git</span><br></pre></td></tr></table></figure>

<p>创建一个新的本地仓库：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git init</span><br></pre></td></tr></table></figure>

<h3 id="添加修改"><a href="#添加修改" class="headerlink" title="添加修改"></a>添加修改</h3><p>添加修改到暂存区：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># 把指定文件添加到暂存区</span><br><span class="line">$ git add xxx</span><br><span class="line"></span><br><span class="line"># 把当前所有修改添加到暂存区</span><br><span class="line">$ git add .</span><br><span class="line"></span><br><span class="line"># 把所有修改添加到暂存区</span><br><span class="line">$ git add -A</span><br></pre></td></tr></table></figure>

<p>提交修改到本地仓库：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># 提交本地的所有修改</span><br><span class="line">$ git commit -a</span><br><span class="line"></span><br><span class="line"># 提交之前已标记的变化</span><br><span class="line">$ git commit</span><br><span class="line"></span><br><span class="line"># 附加消息提交</span><br><span class="line">$ git commit -m &#39;commit message&#39;</span><br></pre></td></tr></table></figure>

<h4 id="储藏"><a href="#储藏" class="headerlink" title="储藏"></a>储藏</h4><p>有时，我们需要在同一个项目的不同分支上工作。当需要切换分支时，偏偏本地的工作还没有完成，此时，提交修改显得不严谨，但是不提交代码又无法切换分支。这时，你可以使用 <code>git stash</code> 将本地的修改内容作为草稿储藏起来。</p>
<p>官方称之为储藏，但我个人更喜欢称之为存草稿。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"># 1. 将修改作为当前分支的草稿保存</span><br><span class="line">$ git stash</span><br><span class="line"></span><br><span class="line"># 2. 查看草稿列表</span><br><span class="line">$ git stash list</span><br><span class="line">stash@&#123;0&#125;: WIP on master: 6fae349 :memo: Writing docs.</span><br><span class="line"></span><br><span class="line"># 3.1 删除草稿</span><br><span class="line">$ git stash drop stash@&#123;0&#125;</span><br><span class="line"></span><br><span class="line"># 3.2 读取草稿</span><br><span class="line">$ git stash apply stash@&#123;0&#125;</span><br></pre></td></tr></table></figure>

<h3 id="撤销修改"><a href="#撤销修改" class="headerlink" title="撤销修改"></a>撤销修改</h3><p>撤销本地修改：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"># 移除缓存区的所有文件（i.e. 撤销上次git add）</span><br><span class="line">$ git reset HEAD</span><br><span class="line"></span><br><span class="line"># 将HEAD重置到上一次提交的版本，并将之后的修改标记为未添加到缓存区的修改</span><br><span class="line">$ git reset &lt;commit&gt;</span><br><span class="line"></span><br><span class="line"># 将HEAD重置到上一次提交的版本，并保留未提交的本地修改</span><br><span class="line">$ git reset --keep &lt;commit&gt;</span><br><span class="line"></span><br><span class="line"># 放弃工作目录下的所有修改</span><br><span class="line">$ git reset --hard HEAD</span><br><span class="line"></span><br><span class="line"># 将HEAD重置到指定的版本，并抛弃该版本之后的所有修改</span><br><span class="line">$ git reset --hard &lt;commit-hash&gt;</span><br><span class="line"></span><br><span class="line"># 用远端分支强制覆盖本地分支</span><br><span class="line">$ git reset --hard &lt;remote&#x2F;branch&gt; e.g., upstream&#x2F;master, origin&#x2F;my-feature</span><br><span class="line"></span><br><span class="line"># 放弃某个文件的所有本地修改</span><br><span class="line">$ git checkout HEAD &lt;file&gt;</span><br></pre></td></tr></table></figure>

<p>删除添加<code>.gitignore</code>文件前错误提交的文件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ git rm -r --cached .</span><br><span class="line">$ git add .</span><br><span class="line">$ git commit -m &quot;remove xyz file&quot;</span><br></pre></td></tr></table></figure>

<p>撤销远程修改（创建一个新的提交，并回滚到指定版本）：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git revert &lt;commit-hash&gt;</span><br></pre></td></tr></table></figure>

<p>彻底删除指定版本：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># 执行下面命令后，commit-hash 提交后的记录都会被彻底删除，使用需谨慎</span><br><span class="line">$ git reset --hard &lt;commit-hash&gt;</span><br><span class="line">$ git push -f</span><br></pre></td></tr></table></figure>

<h3 id="更新与推送"><a href="#更新与推送" class="headerlink" title="更新与推送"></a>更新与推送</h3><p>更新：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># 下载远程端版本，但不合并到HEAD中</span><br><span class="line">$ git fetch &lt;remote&gt;</span><br><span class="line"></span><br><span class="line"># 将远程端版本合并到本地版本中</span><br><span class="line">$ git pull origin master</span><br><span class="line"></span><br><span class="line"># 以rebase方式将远端分支与本地合并</span><br><span class="line">$ git pull --rebase &lt;remote&gt; &lt;branch&gt;</span><br></pre></td></tr></table></figure>

<p>推送：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"># 将本地版本推送到远程端</span><br><span class="line">$ git push remote &lt;remote&gt; &lt;branch&gt;</span><br><span class="line"></span><br><span class="line"># 删除远程端分支</span><br><span class="line">$ git push &lt;remote&gt; :&lt;branch&gt; (since Git v1.5.0)</span><br><span class="line">$ git push &lt;remote&gt; --delete &lt;branch&gt; (since Git v1.7.0)</span><br><span class="line"></span><br><span class="line"># 发布标签</span><br><span class="line">$ git push --tags</span><br></pre></td></tr></table></figure>

<h3 id="查看信息"><a href="#查看信息" class="headerlink" title="查看信息"></a>查看信息</h3><p>显示工作路径下已修改的文件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git status</span><br></pre></td></tr></table></figure>

<p>显示与上次提交版本文件的不同：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git diff</span><br></pre></td></tr></table></figure>

<p>显示提交历史：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># 从最新提交开始，显示所有的提交记录（显示hash， 作者信息，提交的标题和时间）</span><br><span class="line">$ git log</span><br><span class="line"></span><br><span class="line"># 显示某个用户的所有提交</span><br><span class="line">$ git log --author&#x3D;&quot;username&quot;</span><br><span class="line"></span><br><span class="line"># 显示某个文件的所有修改</span><br><span class="line">$ git log -p &lt;file&gt;</span><br></pre></td></tr></table></figure>

<p>显示搜索内容：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># 从当前目录的所有文件中查找文本内容</span><br><span class="line">$ git grep &quot;Hello&quot;</span><br><span class="line"></span><br><span class="line"># 在某一版本中搜索文本</span><br><span class="line">$ git grep &quot;Hello&quot; v2.5</span><br></pre></td></tr></table></figure>

<h3 id="分支"><a href="#分支" class="headerlink" title="分支"></a>分支</h3><p>增删查分支：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"># 列出所有的分支</span><br><span class="line">$ git branch</span><br><span class="line"></span><br><span class="line"># 列出所有的远端分支</span><br><span class="line">$ git branch -r</span><br><span class="line"></span><br><span class="line"># 基于当前分支创建新分支</span><br><span class="line">$ git branch &lt;new-branch&gt;</span><br><span class="line"></span><br><span class="line"># 基于远程分支创建新的可追溯的分支</span><br><span class="line">$ git branch --track &lt;new-branch&gt; &lt;remote-branch&gt;</span><br><span class="line"></span><br><span class="line"># 删除本地分支</span><br><span class="line">$ git branch -d &lt;branch&gt;</span><br><span class="line"></span><br><span class="line"># 强制删除本地分支，将会丢失未合并的修改</span><br><span class="line">$ git branch -D &lt;branch&gt;</span><br></pre></td></tr></table></figure>

<p>切换分支：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># 切换分支</span><br><span class="line">$ git checkout &lt;branch&gt;</span><br><span class="line"></span><br><span class="line"># 创建并切换到新分支</span><br><span class="line">$ git checkout -b &lt;branch&gt;</span><br></pre></td></tr></table></figure>

<h3 id="标签"><a href="#标签" class="headerlink" title="标签"></a>标签</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># 给当前版本打标签</span><br><span class="line">$ git tag &lt;tag-name&gt;</span><br><span class="line"></span><br><span class="line"># 给当前版本打标签并附加消息</span><br><span class="line">$ git tag -a &lt;tag-name&gt;</span><br></pre></td></tr></table></figure>

<h3 id="合并与重置"><a href="#合并与重置" class="headerlink" title="合并与重置"></a>合并与重置</h3><blockquote>
<p>merge 与 rebase 虽然是 git 常用功能，但是强烈建议不要使用 git 命令来完成这项工作。</p>
<p>因为如果出现代码冲突，在没有代码比对工具的情况下，实在太艰难了。</p>
<p>你可以考虑使用各种 Git GUI 工具。</p>
</blockquote>
<p>合并：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 将分支合并到当前HEAD中</span><br><span class="line">$ git merge &lt;branch&gt;</span><br></pre></td></tr></table></figure>

<p>重置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 将当前HEAD版本重置到分支中，请勿重置已发布的提交</span><br><span class="line">$ git rebase &lt;branch&gt;</span><br></pre></td></tr></table></figure>

<h3 id="Github"><a href="#Github" class="headerlink" title="Github"></a>Github</h3><p>Github 作为最著名的代码开源协作社区，在程序员圈想必无人不知，无人不晓。</p>
<p>这里不赘述 Github 的用法，确实有不会用的新手同学，可以参考官方教程：<a target="_blank" rel="noopener" href="https://guides.github.com/">https://guides.github.com/</a></p>
<h4 id="clone-方式"><a href="#clone-方式" class="headerlink" title="clone 方式"></a>clone 方式</h4><p>Git 支持三种协议：HTTPS / SSH / GIT</p>
<p>而 Github 上支持 HTTPS 和 SSH。</p>
<p>HTTPS 这种方式要求你每次 push 时都要输入用户名、密码，有些繁琐。</p>
<p>而 SSH 要求你本地生成证书，然后在你的 Github 账户中注册。第一次配置麻烦是麻烦了点，但是以后就免去了每次 push 需要输入用户名、密码的繁琐。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601170309820.webp" alt="img"></p>
<p>以下介绍以下，如何生成证书，以及在 Github 中注册。</p>
<h4 id="生成-SSH-公钥"><a href="#生成-SSH-公钥" class="headerlink" title="生成 SSH 公钥"></a>生成 SSH 公钥</h4><p>如前所述，许多 Git 服务器都使用 SSH 公钥进行认证。为了向 Git 服务器提供 SSH 公钥，如果某系统用户尚未拥有密钥，必须事先为其生成一份。这个过程在所有操作系统上都是相似的。首先，你需要确认自己是否已经拥有密钥。默认情况下，用户的 SSH 密钥存储在其 <code>\~/.ssh</code> 目录下。进入该目录并列出其中内容，你便可以快速确认自己是否已拥有密钥：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ cd ~&#x2F;.ssh</span><br><span class="line">$ ls</span><br><span class="line">authorized_keys2  id_dsa       known_hosts</span><br><span class="line">config            id_dsa.pub</span><br></pre></td></tr></table></figure>

<p>我们需要寻找一对以 <code>id_dsa</code> 或 <code>id_rsa</code> 命名的文件，其中一个带有 <code>.pub</code> 扩展名。 <code>.pub</code> 文件是你的公钥，另一个则是私钥。如果找不到这样的文件（或者根本没有 <code>.ssh</code> 目录），你可以通过运行 <code>ssh-keygen</code> 程序来创建它们。在 Linux/Mac 系统中，<code>ssh-keygen</code> 随 SSH 软件包提供；在 Windows 上，该程序包含于 MSysGit 软件包中。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$ ssh-keygen</span><br><span class="line">Generating public&#x2F;private rsa key pair.</span><br><span class="line">Enter file in which to save the key (&#x2F;home&#x2F;schacon&#x2F;.ssh&#x2F;id_rsa):</span><br><span class="line">Created directory &#39;&#x2F;home&#x2F;schacon&#x2F;.ssh&#39;.</span><br><span class="line">Enter passphrase (empty for no passphrase):</span><br><span class="line">Enter same passphrase again:</span><br><span class="line">Your identification has been saved in &#x2F;home&#x2F;schacon&#x2F;.ssh&#x2F;id_rsa.</span><br><span class="line">Your public key has been saved in &#x2F;home&#x2F;schacon&#x2F;.ssh&#x2F;id_rsa.pub.</span><br><span class="line">The key fingerprint is:</span><br><span class="line">d0:82:24:8e:d7:f1:bb:9b:33:53:96:93:49:da:9b:e3 schacon@mylaptop.local</span><br></pre></td></tr></table></figure>

<p>首先 <code>ssh-keygen</code> 会确认密钥的存储位置（默认是 <code>.ssh/id_rsa</code>），然后它会要求你输入两次密钥口令。如果你不想在使用密钥时输入口令，将其留空即可。</p>
<p>现在，进行了上述操作的用户需要将各自的公钥发送给任意一个 Git 服务器管理员（假设服务器正在使用基于公钥的 SSH 验证设置）。他们所要做的就是复制各自的 <code>.pub</code> 文件内容，并将其通过邮件发送。公钥看起来是这样的：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ cat ~&#x2F;.ssh&#x2F;id_rsa.pub</span><br><span class="line">ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom&#x2F;BWDSU</span><br><span class="line">GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3</span><br><span class="line">Pbv7kOdJ&#x2F;MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK&#x2F;7XA</span><br><span class="line">t3FaoJoAsncM1Q9x5+3V0Ww68&#x2F;eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw&#x2F;Pb0rwert&#x2F;En</span><br><span class="line">mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z&#x2F;il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx</span><br><span class="line">NrRFi9wrf+M7Q&#x3D;&#x3D; schacon@mylaptop.local</span><br></pre></td></tr></table></figure>

<p>在你的 Github 账户中，依次点击 <strong>Settings</strong> &gt; <strong>SSH and GPG keys</strong> &gt; <strong>New SSH key</strong></p>
<p>然后，将上面生成的公钥内容粘贴到 <code>Key</code> 编辑框并保存。至此大功告成。</p>
<p>后面，你在克隆你的 Github 项目时使用 SSH 方式即可。</p>
<p>如果觉得我的讲解还不够细致，可以参考：<a target="_blank" rel="noopener" href="https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/">https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/</a></p>
<h2 id="最佳实践-Git-Flow"><a href="#最佳实践-Git-Flow" class="headerlink" title="最佳实践 Git Flow"></a>最佳实践 Git Flow</h2><blockquote>
<p>详细内容，可以参考这篇文章：Git 在团队中的最佳实践 – 如何正确使用 Git Flow</p>
</blockquote>
<p>Git 在实际开发中的最佳实践策略 Git Flow 可以归纳为以下：</p>
<ul>
<li><strong><code>master</code> 分支</strong> - 也就是我们经常使用的主线分支，这个分支是最近发布到生产环境的代码，这个分支只能从其他分支合并，不能在这个分支直接修改。</li>
<li><strong><code>develop</code> 分支</strong> - 这个分支是我们的主开发分支，包含所有要发布到下一个 release 的代码，这个分支主要是从其他分支合并代码过来，比如 feature 分支。</li>
<li><strong><code>feature</code> 分支</strong> - 这个分支主要是用来开发一个新的功能，一旦开发完成，我们合并回 develop 分支进入下一个 release。</li>
<li><strong><code>release</code> 分支</strong> - 当你需要一个发布一个新 release 的时候，我们基于 Develop 分支创建一个 release 分支，完成 release 后，我们合并到 master 和 develop 分支。</li>
<li><strong><code>hotfix</code> 分支</strong> - 当我们在 master 发现新的 Bug 时候，我们需要创建一个 hotfix, 完成 hotfix 后，我们合并回 master 和 develop 分支，所以 hotfix 的改动会进入下一个 release。</li>
</ul>
<h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="编辑提交-editting-commits"><a href="#编辑提交-editting-commits" class="headerlink" title="编辑提交 (editting commits)"></a>编辑提交 (editting commits)</h3><h4 id="我刚才提交了什么"><a href="#我刚才提交了什么" class="headerlink" title="我刚才提交了什么"></a>我刚才提交了什么</h4><p>如果你用 <code>git commit -a</code> 提交了一次变化 (changes)，而你又不确定到底这次提交了哪些内容。你就可以用下面的命令显示当前<code>HEAD</code>上的最近一次的提交 (commit):</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git show</span><br></pre></td></tr></table></figure>

<p>或者</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git log -n1 -p</span><br></pre></td></tr></table></figure>

<h4 id="我的提交信息-commit-message-写错了"><a href="#我的提交信息-commit-message-写错了" class="headerlink" title="我的提交信息 (commit message) 写错了"></a>我的提交信息 (commit message) 写错了</h4><p>如果你的提交信息 (commit message) 写错了且这次提交 (commit) 还没有推(push), 你可以通过下面的方法来修改提交信息(commit message):</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git commit --amend</span><br></pre></td></tr></table></figure>

<p>这会打开你的默认编辑器, 在这里你可以编辑信息. 另一方面, 你也可以用一条命令一次完成:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git commit --amend -m &#39;xxxxxxx&#39;</span><br></pre></td></tr></table></figure>

<p>如果你已经推 (push) 了这次提交 (commit), 你可以修改这次提交(commit) 然后强推(force push), 但是不推荐这么做。</p>
<h4 id="我提交-commit-里的用户名和邮箱不对"><a href="#我提交-commit-里的用户名和邮箱不对" class="headerlink" title="我提交 (commit) 里的用户名和邮箱不对"></a>我提交 (commit) 里的用户名和邮箱不对</h4><p>如果这只是单个提交 (commit)，修改它：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git commit --amend --author &quot;New Authorname &lt;authoremail@mydomain.com&gt;&quot;</span><br></pre></td></tr></table></figure>

<p>如果你需要修改所有历史, 参考’git filter-branch’的指南页.</p>
<h4 id="我想从一个提交-commit-里移除一个文件"><a href="#我想从一个提交-commit-里移除一个文件" class="headerlink" title="我想从一个提交 (commit) 里移除一个文件"></a>我想从一个提交 (commit) 里移除一个文件</h4><p>通过下面的方法，从一个提交 (commit) 里移除一个文件:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ git checkout HEAD^ myfile</span><br><span class="line">$ git add -A</span><br><span class="line">$ git commit --amend</span><br></pre></td></tr></table></figure>

<p>这将非常有用，当你有一个开放的补丁 (open patch)，你往上面提交了一个不必要的文件，你需要强推(force push) 去更新这个远程补丁。</p>
<h4 id="我想删除我的的最后一次提交-commit"><a href="#我想删除我的的最后一次提交-commit" class="headerlink" title="我想删除我的的最后一次提交 (commit)"></a>我想删除我的的最后一次提交 (commit)</h4><p>如果你需要删除推了的提交 (pushed commits)，你可以使用下面的方法。可是，这会不可逆的改变你的历史，也会搞乱那些已经从该仓库拉取(pulled) 了的人的历史。简而言之，如果你不是很确定，千万不要这么做。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git reset HEAD^ --hard</span><br><span class="line">$ git push -f [remote] [branch]</span><br></pre></td></tr></table></figure>

<p>如果你还没有推到远程, 把 Git 重置 (reset) 到你最后一次提交前的状态就可以了(同时保存暂存的变化):</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(my-branch*)$ git reset --soft HEAD@&#123;1&#125;</span><br></pre></td></tr></table></figure>

<p>这只能在没有推送之前有用. 如果你已经推了, 唯一安全能做的是 <code>git revert SHAofBadCommit</code>， 那会创建一个新的提交 (commit) 用于撤消前一个提交的所有变化(changes)；或者, 如果你推的这个分支是 rebase-safe 的 (例如：其它开发者不会从这个分支拉), 只需要使用 <code>git push -f</code>；更多, 请参考 the above section。</p>
<h4 id="删除任意提交-commit"><a href="#删除任意提交-commit" class="headerlink" title="删除任意提交 (commit)"></a>删除任意提交 (commit)</h4><p>同样的警告：不到万不得已的时候不要这么做.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT</span><br><span class="line">$ git push -f [remote] [branch]</span><br></pre></td></tr></table></figure>

<p>或者做一个 交互式 rebase 删除那些你想要删除的提交 (commit) 里所对应的行。</p>
<h4 id="我尝试推一个修正后的提交-amended-commit-到远程，但是报错："><a href="#我尝试推一个修正后的提交-amended-commit-到远程，但是报错：" class="headerlink" title="我尝试推一个修正后的提交 (amended commit) 到远程，但是报错："></a>我尝试推一个修正后的提交 (amended commit) 到远程，但是报错：</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">To https:&#x2F;&#x2F;github.com&#x2F;yourusername&#x2F;repo.git</span><br><span class="line">! [rejected]        mybranch -&gt; mybranch (non-fast-forward)</span><br><span class="line">error: failed to push some refs to &#39;https:&#x2F;&#x2F;github.com&#x2F;tanay1337&#x2F;webmaker.org.git&#39;</span><br><span class="line">hint: Updates were rejected because the tip of your current branch is behind</span><br><span class="line">hint: its remote counterpart. Integrate the remote changes (e.g.</span><br><span class="line">hint: &#39;git pull ...&#39;) before pushing again.</span><br><span class="line">hint: See the &#39;Note about fast-forwards&#39; in &#39;git push --help&#39; for details.</span><br></pre></td></tr></table></figure>

<p>注意, rebasing(见下面)和修正 (amending) 会用一个<strong>新的提交 (commit) 代替旧的</strong>, 所以如果之前你已经往远程仓库上推过一次修正前的提交 (commit)，那你现在就必须强推 (force push) (<code>-f</code>)。注意 – 总是 确保你指明一个分支!</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(my-branch)$ git push origin mybranch -f</span><br></pre></td></tr></table></figure>

<p>一般来说, <strong>要避免强推</strong>. 最好是创建和推 (push) 一个新的提交(commit)，而不是强推一个修正后的提交。后者会使那些与该分支或该分支的子分支工作的开发者，在源历史中产生冲突。</p>
<h4 id="我意外的做了一次硬重置-hard-reset-，我想找回我的内容"><a href="#我意外的做了一次硬重置-hard-reset-，我想找回我的内容" class="headerlink" title="我意外的做了一次硬重置 (hard reset)，我想找回我的内容"></a>我意外的做了一次硬重置 (hard reset)，我想找回我的内容</h4><p>如果你意外的做了 <code>git reset --hard</code>, 你通常能找回你的提交 (commit), 因为 Git 对每件事都会有日志，且都会保存几天。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git reflog</span><br></pre></td></tr></table></figure>

<p>你将会看到一个你过去提交 (commit) 的列表, 和一个重置的提交。选择你想要回到的提交 (commit) 的 SHA，再重置一次:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git reset --hard SHA1234</span><br></pre></td></tr></table></figure>

<p>这样就完成了。</p>
<h3 id="暂存-Staging"><a href="#暂存-Staging" class="headerlink" title="暂存 (Staging)"></a>暂存 (Staging)</h3><h4 id="我需要把暂存的内容添加到上一次的提交-commit"><a href="#我需要把暂存的内容添加到上一次的提交-commit" class="headerlink" title="我需要把暂存的内容添加到上一次的提交 (commit)"></a>我需要把暂存的内容添加到上一次的提交 (commit)</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(my-branch*)$ git commit --amend</span><br></pre></td></tr></table></figure>

<h4 id="我想要暂存一个新文件的一部分，而不是这个文件的全部"><a href="#我想要暂存一个新文件的一部分，而不是这个文件的全部" class="headerlink" title="我想要暂存一个新文件的一部分，而不是这个文件的全部"></a>我想要暂存一个新文件的一部分，而不是这个文件的全部</h4><p>一般来说, 如果你想暂存一个文件的一部分, 你可这样做:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git add --patch filename.x</span><br></pre></td></tr></table></figure>

<p><code>-p</code> 简写。这会打开交互模式， 你将能够用 <code>s</code> 选项来分隔提交 (commit)；然而, 如果这个文件是新的, 会没有这个选择， 添加一个新文件时, 这样做:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git add -N filename.x</span><br></pre></td></tr></table></figure>

<p>然后, 你需要用 <code>e</code> 选项来手动选择需要添加的行，执行 <code>git diff --cached</code> 将会显示哪些行暂存了哪些行只是保存在本地了。</p>
<h4 id="我想把在一个文件里的变化-changes-加到两个提交-commit-里"><a href="#我想把在一个文件里的变化-changes-加到两个提交-commit-里" class="headerlink" title="我想把在一个文件里的变化 (changes) 加到两个提交 (commit) 里"></a>我想把在一个文件里的变化 (changes) 加到两个提交 (commit) 里</h4><p><code>git add</code> 会把整个文件加入到一个提交. <code>git add -p</code> 允许交互式的选择你想要提交的部分.</p>
<h4 id="我想把暂存的内容变成未暂存，把未暂存的内容暂存起来"><a href="#我想把暂存的内容变成未暂存，把未暂存的内容暂存起来" class="headerlink" title="我想把暂存的内容变成未暂存，把未暂存的内容暂存起来"></a>我想把暂存的内容变成未暂存，把未暂存的内容暂存起来</h4><p>这个有点困难， 我能想到的最好的方法是先 stash 未暂存的内容， 然后重置 (reset)，再 pop 第一步 stashed 的内容, 最后再 add 它们。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ git stash -k</span><br><span class="line">$ git reset --hard</span><br><span class="line">$ git stash pop</span><br><span class="line">$ git add -A</span><br></pre></td></tr></table></figure>

<h3 id="未暂存-Unstaged-的内容"><a href="#未暂存-Unstaged-的内容" class="headerlink" title="未暂存 (Unstaged) 的内容"></a>未暂存 (Unstaged) 的内容</h3><h4 id="我想把未暂存的内容移动到一个新分支"><a href="#我想把未暂存的内容移动到一个新分支" class="headerlink" title="我想把未暂存的内容移动到一个新分支"></a>我想把未暂存的内容移动到一个新分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git checkout -b my-branch</span><br></pre></td></tr></table></figure>

<h4 id="我想把未暂存的内容移动到另一个已存在的分支"><a href="#我想把未暂存的内容移动到另一个已存在的分支" class="headerlink" title="我想把未暂存的内容移动到另一个已存在的分支"></a>我想把未暂存的内容移动到另一个已存在的分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ git stash</span><br><span class="line">$ git checkout my-branch</span><br><span class="line">$ git stash pop</span><br></pre></td></tr></table></figure>

<h4 id="我想丢弃本地未提交的变化-uncommitted-changes"><a href="#我想丢弃本地未提交的变化-uncommitted-changes" class="headerlink" title="我想丢弃本地未提交的变化 (uncommitted changes)"></a>我想丢弃本地未提交的变化 (uncommitted changes)</h4><p>如果你只是想重置源 (origin) 和你本地 (local) 之间的一些提交(commit)，你可以：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">## one commit</span><br><span class="line">(my-branch)$ git reset --hard HEAD^</span><br><span class="line">## two commits</span><br><span class="line">(my-branch)$ git reset --hard HEAD^^</span><br><span class="line">## four commits</span><br><span class="line">(my-branch)$ git reset --hard HEAD~4</span><br><span class="line">## or</span><br><span class="line">(master)$ git checkout -f</span><br></pre></td></tr></table></figure>

<p>重置某个特殊的文件, 你可以用文件名做为参数:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git reset filename</span><br></pre></td></tr></table></figure>

<h4 id="我想丢弃某些未暂存的内容"><a href="#我想丢弃某些未暂存的内容" class="headerlink" title="我想丢弃某些未暂存的内容"></a>我想丢弃某些未暂存的内容</h4><p>如果你想丢弃工作拷贝中的一部分内容，而不是全部。</p>
<p>签出 (checkout) 不需要的内容，保留需要的。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git checkout -p</span><br><span class="line">## Answer y to all of the snippets you want to drop</span><br></pre></td></tr></table></figure>

<p>另外一个方法是使用 <code>stash</code>， Stash 所有要保留下的内容, 重置工作拷贝, 重新应用保留的部分。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ git stash -p</span><br><span class="line">## Select all of the snippets you want to save</span><br><span class="line">$ git reset --hard</span><br><span class="line">$ git stash pop</span><br></pre></td></tr></table></figure>

<p>或者, stash 你不需要的部分, 然后 stash drop。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ git stash -p</span><br><span class="line">## Select all of the snippets you don&#39;t want to save</span><br><span class="line">$ git stash drop</span><br></pre></td></tr></table></figure>

<h3 id="分支-Branches"><a href="#分支-Branches" class="headerlink" title="分支 (Branches)"></a>分支 (Branches)</h3><h4 id="我从错误的分支拉取了内容，或把内容拉取到了错误的分支"><a href="#我从错误的分支拉取了内容，或把内容拉取到了错误的分支" class="headerlink" title="我从错误的分支拉取了内容，或把内容拉取到了错误的分支"></a>我从错误的分支拉取了内容，或把内容拉取到了错误的分支</h4><p>这是另外一种使用 <code>git reflog</code> 情况，找到在这次错误拉 (pull) 之前 HEAD 的指向。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git reflog</span><br><span class="line">ab7555f HEAD@&#123;0&#125;: pull origin wrong-branch: Fast-forward</span><br><span class="line">c5bc55a HEAD@&#123;1&#125;: checkout: checkout message goes here</span><br></pre></td></tr></table></figure>

<p>重置分支到你所需的提交 (desired commit):</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git reset --hard c5bc55a</span><br></pre></td></tr></table></figure>

<p>完成。</p>
<h4 id="我想扔掉本地的提交-commit-，以便我的分支与远程的保持一致"><a href="#我想扔掉本地的提交-commit-，以便我的分支与远程的保持一致" class="headerlink" title="我想扔掉本地的提交 (commit)，以便我的分支与远程的保持一致"></a>我想扔掉本地的提交 (commit)，以便我的分支与远程的保持一致</h4><p>先确认你没有推 (push) 你的内容到远程。</p>
<p><code>git status</code> 会显示你领先 (ahead) 源(origin)多少个提交:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">(my-branch)$ git status</span><br><span class="line">## On branch my-branch</span><br><span class="line">## Your branch is ahead of &#39;origin&#x2F;my-branch&#39; by 2 commits.</span><br><span class="line">##   (use &quot;git push&quot; to publish your local commits)</span><br><span class="line">#</span><br></pre></td></tr></table></figure>

<p>一种方法是:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git reset --hard origin&#x2F;my-branch</span><br></pre></td></tr></table></figure>

<h4 id="我需要提交到一个新分支，但错误的提交到了-master"><a href="#我需要提交到一个新分支，但错误的提交到了-master" class="headerlink" title="我需要提交到一个新分支，但错误的提交到了 master"></a>我需要提交到一个新分支，但错误的提交到了 master</h4><p>在 master 下创建一个新分支，不切换到新分支, 仍在 master 下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git branch my-branch</span><br></pre></td></tr></table></figure>

<p>把 master 分支重置到前一个提交:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git reset --hard HEAD^</span><br></pre></td></tr></table></figure>

<p><code>HEAD^</code> 是 <code>HEAD^1</code> 的简写，你可以通过指定要设置的<code>HEAD</code>来进一步重置。</p>
<p>或者, 如果你不想使用 <code>HEAD^</code>, 找到你想重置到的提交 (commit) 的 hash(<code>git log</code> 能够完成)， 然后重置到这个 hash。使用<code>git push</code> 同步内容到远程。</p>
<p>例如, master 分支想重置到的提交的 hash 为<code>a13b85e</code>:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git reset --hard a13b85e</span><br><span class="line">HEAD is now at a13b85e</span><br></pre></td></tr></table></figure>

<p>签出 (checkout) 刚才新建的分支继续工作:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git checkout my-branch</span><br></pre></td></tr></table></figure>

<h4 id="我想保留来自另外一个-ref-ish-的整个文件"><a href="#我想保留来自另外一个-ref-ish-的整个文件" class="headerlink" title="我想保留来自另外一个 ref-ish 的整个文件"></a>我想保留来自另外一个 ref-ish 的整个文件</h4><p>假设你正在做一个原型方案 (原文为 working spike (see note)), 有成百的内容，每个都工作得很好。现在, 你提交到了一个分支，保存工作内容:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(solution)$ git add -A &amp;&amp; git commit -m &quot;Adding all changes from this spike into one big commit.&quot;</span><br></pre></td></tr></table></figure>

<p>当你想要把它放到一个分支里 (可能是<code>feature</code>, 或者 <code>develop</code>), 你关心是保持整个文件的完整，你想要一个大的提交分隔成比较小。</p>
<p>假设你有:</p>
<ul>
<li>分支 <code>solution</code>, 拥有原型方案， 领先 <code>develop</code> 分支。</li>
<li>分支 <code>develop</code>, 在这里你应用原型方案的一些内容。</li>
</ul>
<p>我去可以通过把内容拿到你的分支里，来解决这个问题:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(develop)$ git checkout solution -- file1.txt</span><br></pre></td></tr></table></figure>

<p>这会把这个文件内容从分支 <code>solution</code> 拿到分支 <code>develop</code> 里来:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">## On branch develop</span><br><span class="line">## Your branch is up-to-date with &#39;origin&#x2F;develop&#39;.</span><br><span class="line">## Changes to be committed:</span><br><span class="line">##  (use &quot;git reset HEAD &lt;file&gt;...&quot; to unstage)</span><br><span class="line">#</span><br><span class="line">##        modified:   file1.txt</span><br></pre></td></tr></table></figure>

<p>然后, 正常提交。</p>
<p>Note: Spike solutions are made to analyze or solve the problem. These solutions are used for estimation and discarded once everyone gets clear visualization of the problem. ~ Wikipedia.</p>
<h4 id="我把几个提交-commit-提交到了同一个分支，而这些提交应该分布在不同的分支里"><a href="#我把几个提交-commit-提交到了同一个分支，而这些提交应该分布在不同的分支里" class="headerlink" title="我把几个提交 (commit) 提交到了同一个分支，而这些提交应该分布在不同的分支里"></a>我把几个提交 (commit) 提交到了同一个分支，而这些提交应该分布在不同的分支里</h4><p>假设你有一个<code>master</code>分支， 执行<code>git log</code>, 你看到你做过两次提交:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git log</span><br><span class="line"></span><br><span class="line">commit e3851e817c451cc36f2e6f3049db528415e3c114</span><br><span class="line">Author: Alex Lee &lt;alexlee@example.com&gt;</span><br><span class="line">Date:   Tue Jul 22 15:39:27 2014 -0400</span><br><span class="line"></span><br><span class="line">    Bug #21 - Added CSRF protection</span><br><span class="line"></span><br><span class="line">commit 5ea51731d150f7ddc4a365437931cd8be3bf3131</span><br><span class="line">Author: Alex Lee &lt;alexlee@example.com&gt;</span><br><span class="line">Date:   Tue Jul 22 15:39:12 2014 -0400</span><br><span class="line"></span><br><span class="line">    Bug #14 - Fixed spacing on title</span><br><span class="line"></span><br><span class="line">commit a13b85e984171c6e2a1729bb061994525f626d14</span><br><span class="line">Author: Aki Rose &lt;akirose@example.com&gt;</span><br><span class="line">Date:   Tue Jul 21 01:12:48 2014 -0400</span><br><span class="line"></span><br><span class="line">    First commit</span><br></pre></td></tr></table></figure>

<p>让我们用提交 hash(commit hash) 标记 bug (<code>e3851e8</code> for #21, <code>5ea5173</code> for #14).</p>
<p>首先, 我们把<code>master</code>分支重置到正确的提交 (<code>a13b85e</code>):</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git reset --hard a13b85e</span><br><span class="line">HEAD is now at a13b85e</span><br></pre></td></tr></table></figure>

<p>现在, 我们对 bug #21 创建一个新的分支:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git checkout -b 21</span><br><span class="line">(21)$</span><br></pre></td></tr></table></figure>

<p>接着, 我们用 cherry-pick 把对 bug #21 的提交放入当前分支。这意味着我们将应用 (apply) 这个提交(commit)，仅仅这一个提交(commit)，直接在 HEAD 上面。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(21)$ git cherry-pick e3851e8</span><br></pre></td></tr></table></figure>

<p>这时候, 这里可能会产生冲突， 参见交互式 rebasing 章 <strong>冲突节</strong> 解决冲突.</p>
<p>再者， 我们为 bug #14 创建一个新的分支, 也基于<code>master</code>分支</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">(21)$ git checkout master</span><br><span class="line">(master)$ git checkout -b 14</span><br><span class="line">(14)$</span><br></pre></td></tr></table></figure>

<p>最后, 为 bug #14 执行 <code>cherry-pick</code>:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(14)$ git cherry-pick 5ea5173</span><br></pre></td></tr></table></figure>

<h4 id="我想删除上游-upstream-分支被删除了的本地分支"><a href="#我想删除上游-upstream-分支被删除了的本地分支" class="headerlink" title="我想删除上游 (upstream) 分支被删除了的本地分支"></a>我想删除上游 (upstream) 分支被删除了的本地分支</h4><p>一旦你在 github 上面合并 (merge) 了一个 pull request, 你就可以删除你 fork 里被合并的分支。如果你不准备继续在这个分支里工作, 删除这个分支的本地拷贝会更干净，使你不会陷入工作分支和一堆陈旧分支的混乱之中。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git fetch -p</span><br></pre></td></tr></table></figure>

<h4 id="我不小心删除了我的分支"><a href="#我不小心删除了我的分支" class="headerlink" title="我不小心删除了我的分支"></a>我不小心删除了我的分支</h4><p>如果你定期推送到远程, 多数情况下应该是安全的，但有些时候还是可能删除了还没有推到远程的分支。让我们先创建一个分支和一个新的文件:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git checkout -b my-branch</span><br><span class="line">(my-branch)$ git branch</span><br><span class="line">(my-branch)$ touch foo.txt</span><br><span class="line">(my-branch)$ ls</span><br><span class="line">README.md foo.txt</span><br></pre></td></tr></table></figure>

<p>添加文件并做一次提交</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">(my-branch)$ git add .</span><br><span class="line">(my-branch)$ git commit -m &#39;foo.txt added&#39;</span><br><span class="line">(my-branch)$ foo.txt added</span><br><span class="line"> 1 files changed, 1 insertions(+)</span><br><span class="line"> create mode 100644 foo.txt</span><br><span class="line">(my-branch)$ git log</span><br><span class="line"></span><br><span class="line">commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012</span><br><span class="line">Author: siemiatj &lt;siemiatj@example.com&gt;</span><br><span class="line">Date:   Wed Jul 30 00:34:10 2014 +0200</span><br><span class="line"></span><br><span class="line">    foo.txt added</span><br><span class="line"></span><br><span class="line">commit 69204cdf0acbab201619d95ad8295928e7f411d5</span><br><span class="line">Author: Kate Hudson &lt;katehudson@example.com&gt;</span><br><span class="line">Date:   Tue Jul 29 13:14:46 2014 -0400</span><br><span class="line"></span><br><span class="line">    Fixes #6: Force pushing after amending commits</span><br></pre></td></tr></table></figure>

<p>现在我们切回到主 (master) 分支，‘不小心的’删除<code>my-branch</code>分支</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">(my-branch)$ git checkout master</span><br><span class="line">Switched to branch &#39;master&#39;</span><br><span class="line">Your branch is up-to-date with &#39;origin&#x2F;master&#39;.</span><br><span class="line">(master)$ git branch -D my-branch</span><br><span class="line">Deleted branch my-branch (was 4e3cd85).</span><br><span class="line">(master)$ echo oh noes, deleted my branch!</span><br><span class="line">oh noes, deleted my branch!</span><br></pre></td></tr></table></figure>

<p>在这时候你应该想起了<code>reflog</code>, 一个升级版的日志，它存储了仓库 (repo) 里面所有动作的历史。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git reflog</span><br><span class="line">69204cd HEAD@&#123;0&#125;: checkout: moving from my-branch to master</span><br><span class="line">4e3cd85 HEAD@&#123;1&#125;: commit: foo.txt added</span><br><span class="line">69204cd HEAD@&#123;2&#125;: checkout: moving from master to my-branch</span><br></pre></td></tr></table></figure>

<p>正如你所见，我们有一个来自删除分支的提交 hash(commit hash)，接下来看看是否能恢复删除了的分支。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git checkout -b my-branch-help</span><br><span class="line">Switched to a new branch &#39;my-branch-help&#39;</span><br><span class="line">(my-branch-help)$ git reset --hard 4e3cd85</span><br><span class="line">HEAD is now at 4e3cd85 foo.txt added</span><br><span class="line">(my-branch-help)$ ls</span><br><span class="line">README.md foo.txt</span><br></pre></td></tr></table></figure>

<p>看! 我们把删除的文件找回来了。Git 的 <code>reflog</code> 在 rebasing 出错的时候也是同样有用的。</p>
<h4 id="我想删除一个分支"><a href="#我想删除一个分支" class="headerlink" title="我想删除一个分支"></a>我想删除一个分支</h4><p>删除一个远程分支:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git push origin --delete my-branch</span><br></pre></td></tr></table></figure>

<p>你也可以:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git push origin :my-branch</span><br></pre></td></tr></table></figure>

<p>删除一个本地分支:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git branch -D my-branch</span><br></pre></td></tr></table></figure>

<h4 id="我想从别人正在工作的远程分支签出-checkout-一个分支"><a href="#我想从别人正在工作的远程分支签出-checkout-一个分支" class="headerlink" title="我想从别人正在工作的远程分支签出 (checkout) 一个分支"></a>我想从别人正在工作的远程分支签出 (checkout) 一个分支</h4><p>首先, 从远程拉取 (fetch) 所有分支:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git fetch --all</span><br></pre></td></tr></table></figure>

<p>假设你想要从远程的<code>daves</code>分支签出到本地的<code>daves</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git checkout --track origin&#x2F;daves</span><br><span class="line">Branch daves set up to track remote branch daves from origin.</span><br><span class="line">Switched to a new branch &#39;daves&#39;</span><br></pre></td></tr></table></figure>

<p>(<code>--track</code> 是 <code>git checkout -b [branch] [remotename]/[branch]</code> 的简写)</p>
<p>这样就得到了一个<code>daves</code>分支的本地拷贝, 任何推过 (pushed) 的更新，远程都能看到.</p>
<h3 id="Rebasing-和合并-Merging"><a href="#Rebasing-和合并-Merging" class="headerlink" title="Rebasing 和合并 (Merging)"></a>Rebasing 和合并 (Merging)</h3><h4 id="我想撤销-rebase-merge"><a href="#我想撤销-rebase-merge" class="headerlink" title="我想撤销 rebase/merge"></a>我想撤销 rebase/merge</h4><p>你可以合并 (merge) 或 rebase 了一个错误的分支, 或者完成不了一个进行中的 rebase/merge。Git 在进行危险操作的时候会把原始的 HEAD 保存在一个叫 ORIG_HEAD 的变量里, 所以要把分支恢复到 rebase/merge 前的状态是很容易的。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(my-branch)$ git reset --hard ORIG_HEAD</span><br></pre></td></tr></table></figure>

<h4 id="我已经-rebase-过-但是我不想强推-force-push"><a href="#我已经-rebase-过-但是我不想强推-force-push" class="headerlink" title="我已经 rebase 过, 但是我不想强推 (force push)"></a>我已经 rebase 过, 但是我不想强推 (force push)</h4><p>不幸的是，如果你想把这些变化 (changes) 反应到远程分支上，你就必须得强推 (force push)。是因你快进(Fast forward) 了提交，改变了 Git 历史, 远程分支不会接受变化 (changes)，除非强推(force push)。这就是许多人使用 merge 工作流, 而不是 rebasing 工作流的主要原因之一， 开发者的强推(force push) 会使大的团队陷入麻烦。使用时需要注意，一种安全使用 rebase 的方法是，不要把你的变化 (changes) 反映到远程分支上, 而是按下面的做:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git checkout my-branch</span><br><span class="line">(my-branch)$ git rebase -i master</span><br><span class="line">(my-branch)$ git checkout master</span><br><span class="line">(master)$ git merge --ff-only my-branch</span><br></pre></td></tr></table></figure>

<p>更多, 参见 this SO thread.</p>
<h4 id="我需要组合-combine-几个提交-commit"><a href="#我需要组合-combine-几个提交-commit" class="headerlink" title="我需要组合 (combine) 几个提交(commit)"></a>我需要组合 (combine) 几个提交(commit)</h4><p>假设你的工作分支将会做对于 <code>master</code> 的 pull-request。一般情况下你不关心提交 (commit) 的时间戳，只想组合 所有 提交 (commit) 到一个单独的里面, 然后重置(reset) 重提交 (recommit)。确保主(master) 分支是最新的和你的变化都已经提交了, 然后:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(my-branch)$ git reset --soft master</span><br><span class="line">(my-branch)$ git commit -am &quot;New awesome feature&quot;</span><br></pre></td></tr></table></figure>

<p>如果你想要更多的控制, 想要保留时间戳, 你需要做交互式 rebase (interactive rebase):</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(my-branch)$ git rebase -i master</span><br></pre></td></tr></table></figure>

<p>如果没有相对的其它分支， 你将不得不相对自己的<code>HEAD</code> 进行 rebase。例如：你想组合最近的两次提交 (commit), 你将相对于<code>HEAD\~2</code> 进行 rebase， 组合最近 3 次提交 (commit), 相对于<code>HEAD\~3</code>, 等等。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git rebase -i HEAD~2</span><br></pre></td></tr></table></figure>

<p>在你执行了交互式 rebase 的命令 (interactive rebase command) 后, 你将在你的编辑器里看到类似下面的内容:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">pick a9c8a1d Some refactoring</span><br><span class="line">pick 01b2fd8 New awesome feature</span><br><span class="line">pick b729ad5 fixup</span><br><span class="line">pick e3851e8 another fix</span><br><span class="line"></span><br><span class="line">## Rebase 8074d12..b729ad5 onto 8074d12</span><br><span class="line">#</span><br><span class="line">## Commands:</span><br><span class="line">##  p, pick &#x3D; use commit</span><br><span class="line">##  r, reword &#x3D; use commit, but edit the commit message</span><br><span class="line">##  e, edit &#x3D; use commit, but stop for amending</span><br><span class="line">##  s, squash &#x3D; use commit, but meld into previous commit</span><br><span class="line">##  f, fixup &#x3D; like &quot;squash&quot;, but discard this commit&#39;s log message</span><br><span class="line">##  x, exec &#x3D; run command (the rest of the line) using shell</span><br><span class="line">#</span><br><span class="line">## These lines can be re-ordered; they are executed from top to bottom.</span><br><span class="line">#</span><br><span class="line">## If you remove a line here THAT COMMIT WILL BE LOST.</span><br><span class="line">#</span><br><span class="line">## However, if you remove everything, the rebase will be aborted.</span><br><span class="line">#</span><br><span class="line">## Note that empty commits are commented out</span><br></pre></td></tr></table></figure>

<p>所有以 <code>#</code> 开头的行都是注释, 不会影响 rebase.</p>
<p>然后，你可以用任何上面命令列表的命令替换 <code>pick</code>, 你也可以通过删除对应的行来删除一个提交 (commit)。</p>
<p>例如, 如果你想 <strong>单独保留最旧 (first) 的提交(commit), 组合所有剩下的到第二个里面</strong>, 你就应该编辑第二个提交 (commit) 后面的每个提交(commit) 前的单词为 <code>f</code>:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">pick a9c8a1d Some refactoring</span><br><span class="line">pick 01b2fd8 New awesome feature</span><br><span class="line">f b729ad5 fixup</span><br><span class="line">f e3851e8 another fix</span><br></pre></td></tr></table></figure>

<p>如果你想组合这些提交 (commit) <strong>并重命名这个提交 (commit)</strong>, 你应该在第二个提交 (commit) 旁边添加一个<code>r</code>，或者更简单的用<code>s</code> 替代 <code>f</code>:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">pick a9c8a1d Some refactoring</span><br><span class="line">pick 01b2fd8 New awesome feature</span><br><span class="line">s b729ad5 fixup</span><br><span class="line">s e3851e8 another fix</span><br></pre></td></tr></table></figure>

<p>你可以在接下来弹出的文本提示框里重命名提交 (commit)。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Newer, awesomer features</span><br><span class="line"></span><br><span class="line">## Please enter the commit message for your changes. Lines starting</span><br><span class="line">## with &#39;#&#39; will be ignored, and an empty message aborts the commit.</span><br><span class="line">## rebase in progress; onto 8074d12</span><br><span class="line">## You are currently editing a commit while rebasing branch &#39;master&#39; on &#39;8074d12&#39;.</span><br><span class="line">#</span><br><span class="line">## Changes to be committed:</span><br><span class="line">#	modified:   README.md</span><br><span class="line">#</span><br></pre></td></tr></table></figure>

<p>如果成功了, 你应该看到类似下面的内容:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ Successfully rebased and updated refs&#x2F;heads&#x2F;master.</span><br></pre></td></tr></table></figure>

<h5 id="安全合并-merging-策略"><a href="#安全合并-merging-策略" class="headerlink" title="安全合并 (merging) 策略"></a>安全合并 (merging) 策略</h5><p><code>--no-commit</code> 执行合并 (merge) 但不自动提交, 给用户在做提交前检查和修改的机会。 <code>no-ff</code> 会为特性分支 (feature branch) 的存在过留下证据, 保持项目历史一致。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git merge --no-ff --no-commit my-branch</span><br></pre></td></tr></table></figure>

<h5 id="我需要将一个分支合并成一个提交-commit"><a href="#我需要将一个分支合并成一个提交-commit" class="headerlink" title="我需要将一个分支合并成一个提交 (commit)"></a>我需要将一个分支合并成一个提交 (commit)</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git merge --squash my-branch</span><br></pre></td></tr></table></figure>

<h5 id="我只想组合-combine-未推的提交-unpushed-commit"><a href="#我只想组合-combine-未推的提交-unpushed-commit" class="headerlink" title="我只想组合 (combine) 未推的提交(unpushed commit)"></a>我只想组合 (combine) 未推的提交(unpushed commit)</h5><p>有时候，在将数据推向上游之前，你有几个正在进行的工作提交 (commit)。这时候不希望把已经推(push) 过的组合进来，因为其他人可能已经有提交 (commit) 引用它们了。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git rebase -i @&#123;u&#125;</span><br></pre></td></tr></table></figure>

<p>这会产生一次交互式的 rebase(interactive rebase), 只会列出没有推 (push) 的提交(commit)， 在这个列表时进行 reorder/fix/squash 都是安全的。</p>
<h4 id="检查是否分支上的所有提交-commit-都合并-merge-过了"><a href="#检查是否分支上的所有提交-commit-都合并-merge-过了" class="headerlink" title="检查是否分支上的所有提交 (commit) 都合并 (merge) 过了"></a>检查是否分支上的所有提交 (commit) 都合并 (merge) 过了</h4><p>检查一个分支上的所有提交 (commit) 是否都已经合并 (merge) 到了其它分支, 你应该在这些分支的 head(或任何 commits)之间做一次 diff:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature&#x2F;120-on-scroll</span><br></pre></td></tr></table></figure>

<p>这会告诉你在一个分支里有而另一个分支没有的所有提交 (commit), 和分支之间不共享的提交(commit) 的列表。另一个做法可以是:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git log master ^feature&#x2F;120-on-scroll --no-merges</span><br></pre></td></tr></table></figure>

<h4 id="交互式-rebase-interactive-rebase-可能出现的问题"><a href="#交互式-rebase-interactive-rebase-可能出现的问题" class="headerlink" title="交互式 rebase(interactive rebase) 可能出现的问题"></a>交互式 rebase(interactive rebase) 可能出现的问题</h4><h5 id="这个-rebase-编辑屏幕出现’noop’"><a href="#这个-rebase-编辑屏幕出现’noop’" class="headerlink" title="这个 rebase 编辑屏幕出现’noop’"></a>这个 rebase 编辑屏幕出现’noop’</h5><p>如果你看到的是这样:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">noop</span><br></pre></td></tr></table></figure>

<p>这意味着你 rebase 的分支和当前分支在同一个提交 (commit) 上, 或者 领先 (ahead) 当前分支。你可以尝试:</p>
<ul>
<li>检查确保主 (master) 分支没有问题</li>
<li>rebase <code>HEAD\~2</code> 或者更早</li>
</ul>
<h5 id="有冲突的情况"><a href="#有冲突的情况" class="headerlink" title="有冲突的情况"></a>有冲突的情况</h5><p>如果你不能成功的完成 rebase, 你可能必须要解决冲突。</p>
<p>首先执行 <code>git status</code> 找出哪些文件有冲突:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">(my-branch)$ git status</span><br><span class="line">On branch my-branch</span><br><span class="line">Changes not staged for commit:</span><br><span class="line">  (use &quot;git add &lt;file&gt;...&quot; to update what will be committed)</span><br><span class="line">  (use &quot;git checkout -- &lt;file&gt;...&quot; to discard changes in working directory)</span><br><span class="line"></span><br><span class="line">	modified:   README.md</span><br></pre></td></tr></table></figure>

<p>在这个例子里面, <code>README.md</code> 有冲突。打开这个文件找到类似下面的内容:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</span><br><span class="line">some code</span><br><span class="line">&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;</span><br><span class="line">some code</span><br><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt; new-commit</span><br></pre></td></tr></table></figure>

<p>你需要解决新提交的代码 (示例里, 从中间<code>==</code>线到<code>new-commit</code>的地方) 与<code>HEAD</code> 之间不一样的地方.</p>
<p>有时候这些合并非常复杂，你应该使用可视化的差异编辑器 (visual diff editor):</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master*)$ git mergetool -t opendiff</span><br></pre></td></tr></table></figure>

<p>在你解决完所有冲突和测试过后, <code>git add</code> 变化了的 (changed) 文件, 然后用<code>git rebase --continue</code> 继续 rebase。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(my-branch)$ git add README.md</span><br><span class="line">(my-branch)$ git rebase --continue</span><br></pre></td></tr></table></figure>

<p>如果在解决完所有的冲突过后，得到了与提交前一样的结果, 可以执行<code>git rebase --skip</code>。</p>
<p>任何时候你想结束整个 rebase 过程，回来 rebase 前的分支状态, 你可以做:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(my-branch)$ git rebase --abort</span><br></pre></td></tr></table></figure>

<h3 id="杂项-Miscellaneous-Objects"><a href="#杂项-Miscellaneous-Objects" class="headerlink" title="杂项 (Miscellaneous Objects)"></a>杂项 (Miscellaneous Objects)</h3><h4 id="克隆所有子模块"><a href="#克隆所有子模块" class="headerlink" title="克隆所有子模块"></a>克隆所有子模块</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git clone --recursive git:&#x2F;&#x2F;github.com&#x2F;foo&#x2F;bar.git</span><br></pre></td></tr></table></figure>

<p>如果已经克隆了:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git submodule update --init --recursive</span><br></pre></td></tr></table></figure>

<h4 id="删除标签-tag"><a href="#删除标签-tag" class="headerlink" title="删除标签 (tag)"></a>删除标签 (tag)</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git tag -d &lt;tag_name&gt;</span><br><span class="line">$ git push &lt;remote&gt; :refs&#x2F;tags&#x2F;&lt;tag_name&gt;</span><br></pre></td></tr></table></figure>

<h4 id="恢复已删除标签-tag"><a href="#恢复已删除标签-tag" class="headerlink" title="恢复已删除标签 (tag)"></a>恢复已删除标签 (tag)</h4><p>如果你想恢复一个已删除标签 (tag), 可以按照下面的步骤: 首先, 需要找到无法访问的标签 (unreachable tag):</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git fsck --unreachable | grep tag</span><br></pre></td></tr></table></figure>

<p>记下这个标签 (tag) 的 hash，然后用 Git 的 update-ref:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git update-ref refs&#x2F;tags&#x2F;&lt;tag_name&gt; &lt;hash&gt;</span><br></pre></td></tr></table></figure>

<p>这时你的标签 (tag) 应该已经恢复了。</p>
<h4 id="已删除补丁-patch"><a href="#已删除补丁-patch" class="headerlink" title="已删除补丁 (patch)"></a>已删除补丁 (patch)</h4><p>如果某人在 GitHub 上给你发了一个 pull request, 但是然后他删除了他自己的原始 fork, 你将没法克隆他们的提交 (commit) 或使用 <code>git am</code>。在这种情况下, 最好手动的查看他们的提交 (commit)，并把它们拷贝到一个本地新分支，然后做提交。</p>
<p>做完提交后, 再修改作者，参见变更作者。然后, 应用变化, 再发起一个新的 pull request。</p>
<h3 id="跟踪文件-Tracking-Files"><a href="#跟踪文件-Tracking-Files" class="headerlink" title="跟踪文件 (Tracking Files)"></a>跟踪文件 (Tracking Files)</h3><h4 id="我只想改变一个文件名字的大小写，而不修改内容"><a href="#我只想改变一个文件名字的大小写，而不修改内容" class="headerlink" title="我只想改变一个文件名字的大小写，而不修改内容"></a>我只想改变一个文件名字的大小写，而不修改内容</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git mv --force myfile MyFile</span><br></pre></td></tr></table></figure>

<h4 id="我想从-Git-删除一个文件，但保留该文件"><a href="#我想从-Git-删除一个文件，但保留该文件" class="headerlink" title="我想从 Git 删除一个文件，但保留该文件"></a>我想从 Git 删除一个文件，但保留该文件</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git rm --cached log.txt</span><br></pre></td></tr></table></figure>

<h3 id="配置-Configuration"><a href="#配置-Configuration" class="headerlink" title="配置 (Configuration)"></a>配置 (Configuration)</h3><h4 id="我想给一些-Git-命令添加别名-alias"><a href="#我想给一些-Git-命令添加别名-alias" class="headerlink" title="我想给一些 Git 命令添加别名 (alias)"></a>我想给一些 Git 命令添加别名 (alias)</h4><p>在 OS X 和 Linux 下, 你的 Git 的配置文件储存在 <code>\~/.gitconfig</code>。我在<code>[alias]</code> 部分添加了一些快捷别名 (和一些我容易拼写错误的)，如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">[alias]</span><br><span class="line">    a &#x3D; add</span><br><span class="line">    amend &#x3D; commit --amend</span><br><span class="line">    c &#x3D; commit</span><br><span class="line">    ca &#x3D; commit --amend</span><br><span class="line">    ci &#x3D; commit -a</span><br><span class="line">    co &#x3D; checkout</span><br><span class="line">    d &#x3D; diff</span><br><span class="line">    dc &#x3D; diff --changed</span><br><span class="line">    ds &#x3D; diff --staged</span><br><span class="line">    f &#x3D; fetch</span><br><span class="line">    loll &#x3D; log --graph --decorate --pretty&#x3D;oneline --abbrev-commit</span><br><span class="line">    m &#x3D; merge</span><br><span class="line">    one &#x3D; log --pretty&#x3D;oneline</span><br><span class="line">    outstanding &#x3D; rebase -i @&#123;u&#125;</span><br><span class="line">    s &#x3D; status</span><br><span class="line">    unpushed &#x3D; log @&#123;u&#125;</span><br><span class="line">    wc &#x3D; whatchanged</span><br><span class="line">    wip &#x3D; rebase -i @&#123;u&#125;</span><br><span class="line">    zap &#x3D; fetch -p</span><br></pre></td></tr></table></figure>

<h4 id="我想缓存一个仓库-repository-的用户名和密码"><a href="#我想缓存一个仓库-repository-的用户名和密码" class="headerlink" title="我想缓存一个仓库 (repository) 的用户名和密码"></a>我想缓存一个仓库 (repository) 的用户名和密码</h4><p>你可能有一个仓库需要授权，这时你可以缓存用户名和密码，而不用每次推 / 拉 (push/pull) 的时候都输入，Credential helper 能帮你。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ git config --global credential.helper cache</span><br><span class="line">## Set git to use the credential memory cache</span><br><span class="line">$ git config --global credential.helper &#39;cache --timeout&#x3D;3600&#39;</span><br><span class="line">## Set the cache to timeout after 1 hour (setting is in seconds)</span><br></pre></td></tr></table></figure>

<h3 id="我不知道我做错了些什么"><a href="#我不知道我做错了些什么" class="headerlink" title="我不知道我做错了些什么"></a>我不知道我做错了些什么</h3><p>你把事情搞砸了：你 <code>重置(reset)</code> 了一些东西, 或者你合并了错误的分支, 亦或你强推了后找不到你自己的提交 (commit) 了。有些时候, 你一直都做得很好, 但你想回到以前的某个状态。</p>
<p>这就是 <code>git reflog</code> 的目的， <code>reflog</code> 记录对分支顶端 (the tip of a branch) 的任何改变, 即使那个顶端没有被任何分支或标签引用。基本上, 每次 HEAD 的改变, 一条新的记录就会增加到<code>reflog</code>。遗憾的是，这只对本地分支起作用，且它只跟踪动作 (例如，不会跟踪一个没有被记录的文件的任何改变)。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">(master)$ git reflog</span><br><span class="line">0a2e358 HEAD@&#123;0&#125;: reset: moving to HEAD\~2</span><br><span class="line">0254ea7 HEAD@&#123;1&#125;: checkout: moving from 2.2 to master</span><br><span class="line">c10f740 HEAD@&#123;2&#125;: checkout: moving from master to 2.2</span><br></pre></td></tr></table></figure>

<p>上面的 reflog 展示了从 master 分支签出 (checkout) 到 2.2 分支，然后再签回。那里，还有一个硬重置 (hard reset) 到一个较旧的提交。最新的动作出现在最上面以 <code>HEAD@&#123;0&#125;</code>标识.</p>
<p>如果事实证明你不小心回移 (move back) 了提交(commit), reflog 会包含你不小心回移前 master 上指向的提交(0254ea7)。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git reset --hard 0254ea7</span><br></pre></td></tr></table></figure>

<p>然后使用 git reset 就可以把 master 改回到之前的 commit，这提供了一个在历史被意外更改情况下的安全网。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/tips/" rel="tag">tips</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-interview/IT运维面试问题总结-简述Etcd、Kubernetes、Lvs、HAProxy"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/interview/IT%E8%BF%90%E7%BB%B4%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93-%E7%AE%80%E8%BF%B0Etcd%E3%80%81Kubernetes%E3%80%81Lvs%E3%80%81HAProxy/"
    >IT运维面试问题总结-简述Etcd、Kubernetes、Lvs、HAProxy.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/interview/IT%E8%BF%90%E7%BB%B4%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93-%E7%AE%80%E8%BF%B0Etcd%E3%80%81Kubernetes%E3%80%81Lvs%E3%80%81HAProxy/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/interview/">interview</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="IT运维面试问题总结-简述Etcd、Kubernetes、Lvs、HAProxy"><a href="#IT运维面试问题总结-简述Etcd、Kubernetes、Lvs、HAProxy" class="headerlink" title="IT运维面试问题总结-简述Etcd、Kubernetes、Lvs、HAProxy"></a>IT运维面试问题总结-简述Etcd、Kubernetes、Lvs、HAProxy</h1><h3 id="简述ETCD及其特点？"><a href="#简述ETCD及其特点？" class="headerlink" title="简述ETCD及其特点？"></a>简述ETCD及其特点？</h3><p>etcd 是 CoreOS 团队发起的开源项目，是一个管理配置信息和服务发现（service discovery）的项目，它的目标是构建一个高可用的分布式键值（key-value）数据库，基于 Go 语言实现。</p>
<p>特点：</p>
<ul>
<li>简单：支持 REST 风格的 HTTP+JSON API</li>
<li>安全：支持 HTTPS 方式的访问</li>
<li>快速：支持并发 1k/s 的写操作</li>
<li>可靠：支持分布式结构，基于 Raft 的一致性算法，Raft 是一套通过选举主节点来实现分布式系统一致性的算法。</li>
</ul>
<h3 id="简述ETCD适应的场景？"><a href="#简述ETCD适应的场景？" class="headerlink" title="简述ETCD适应的场景？"></a>简述ETCD适应的场景？</h3><p>etcd基于其优秀的特点，可广泛的应用于以下场景：</p>
<ul>
<li><code>服务发现</code>(Service Discovery)：服务发现主要解决在同一个分布式集群中的进程或服务，要如何才能找到对方并建立连接。本质上来说，服务发现就是想要了解集群中是否有进程在监听udp或tcp端口，并且通过名字就可以查找和连接。</li>
<li><code>消息发布与订阅</code>：在分布式系统中，最适用的一种组件间通信方式就是消息发布与订阅。即构建一个配置共享中心，数据提供者在这个配置中心发布消息，而消息使用者则订阅他们关心的主题，一旦主题有消息发布，就会实时通知订阅者。通过这种方式可以做到分布式系统配置的集中式管理与动态更新。应用中用到的一些配置信息放到etcd上进行集中管理。</li>
<li><code>负载均衡</code>：在分布式系统中，为了保证服务的高可用以及数据的一致性，通常都会把数据和服务部署多份，以此达到对等服务，即使其中的某一个服务失效了，也不影响使用。etcd本身分布式架构存储的信息访问支持负载均衡。etcd集群化以后，每个etcd的核心节点都可以处理用户的请求。所以，把数据量小但是访问频繁的消息数据直接存储到etcd中也可以实现负载均衡的效果。</li>
<li><code>分布式通知与协调</code>：与消息发布和订阅类似，都用到了etcd中的Watcher机制，通过注册与异步通知机制，实现分布式环境下不同系统之间的通知与协调，从而对数据变更做到实时处理。</li>
<li><code>分布式锁</code>：因为etcd使用Raft算法保持了数据的强一致性，某次操作存储到集群中的值必然是全局一致的，所以很容易实现分布式锁。锁服务有两种使用方式，一是保持独占，二是控制时序。</li>
<li><code>集群监控与Leader竞选</code>：通过etcd来进行监控实现起来非常简单并且实时性强。</li>
</ul>
<h3 id="简述HAProxy及其特性？"><a href="#简述HAProxy及其特性？" class="headerlink" title="简述HAProxy及其特性？"></a>简述HAProxy及其特性？</h3><p>HAProxy是可提供高可用性、负载均衡以及基于TCP和HTTP应用的代理，是免费、快速并且可靠的一种解决方案。HAProxy非常适用于并发大（并发达1w以上）web站点，这些站点通常又需要会话保持或七层处理。HAProxy的运行模式使得它可以很简单安全的整合至当前的架构中，同时可以保护web服务器不被暴露到网络上。</p>
<p>HAProxy的主要特性有：</p>
<ul>
<li>可靠性和稳定性非常好，可以与硬件级的F5负载均衡设备相媲美；</li>
<li>最高可以同时维护40000-50000个并发连接，单位时间内处理的最大请求数为20000个，最大处理能力可达10Git/s；</li>
<li>支持多达8种负载均衡算法，同时也支持会话保持；</li>
<li>支持虚机主机功能，从而实现web负载均衡更加灵活；</li>
<li>支持连接拒绝、全透明代理等独特的功能；</li>
<li>拥有强大的ACL支持，用于访问控制；</li>
<li>其独特的弹性二叉树数据结构，使数据结构的复杂性上升到了0(1)，即数据的查寻速度不会随着数据条目的增加而速度有所下降；</li>
<li>支持客户端的keepalive功能，减少客户端与haproxy的多次三次握手导致资源浪费，让多个请求在一个tcp连接中完成；</li>
<li>支持TCP加速，零复制功能，类似于mmap机制；</li>
<li>支持响应池（response buffering）；</li>
<li>支持RDP协议；</li>
<li>基于源的粘性，类似nginx的ip_hash功能，把来自同一客户端的请求在一定时间内始终调度到上游的同一服务器；</li>
<li>更好统计数据接口，其web接口显示后端集群中各个服务器的接收、发送、拒绝、错误等数据的统计信息；</li>
<li>详细的健康状态检测，web接口中有关于对上游服务器的健康检测状态，并提供了一定的管理功能；</li>
<li>基于流量的健康评估机制；</li>
<li>基于http认证；</li>
<li>基于命令行的管理接口；</li>
<li>日志分析器，可对日志进行分析。</li>
</ul>
<h3 id="简述HAProxy常见的负载均衡策略？"><a href="#简述HAProxy常见的负载均衡策略？" class="headerlink" title="简述HAProxy常见的负载均衡策略？"></a>简述HAProxy常见的负载均衡策略？</h3><p>HAProxy负载均衡策略非常多，常见的有如下8种：</p>
<ul>
<li>roundrobin：表示简单的轮询。</li>
<li>static-rr：表示根据权重。</li>
<li>leastconn：表示最少连接者先处理。</li>
<li>source：表示根据请求的源IP，类似Nginx的IP_hash机制。</li>
<li>ri：表示根据请求的URI。</li>
<li>rl_param：表示根据HTTP请求头来锁定每一次HTTP请求。</li>
<li>rdp-cookie(name)：表示根据据cookie(name)来锁定并哈希每一次TCP请求。</li>
</ul>
<h3 id="简述负载均衡四层和七层的区别？"><a href="#简述负载均衡四层和七层的区别？" class="headerlink" title="简述负载均衡四层和七层的区别？"></a>简述负载均衡四层和七层的区别？</h3><p><code>四层负载均衡器</code>也称为4层交换机，主要通过分析IP层及TCP/UDP层的流量实现基于IP加端口的负载均衡，如常见的LVS、F5等；</p>
<p><code>七层负载均衡器</code>也称为7层交换机，位于OSI的最高层，即应用层，此负载均衡器支持多种协议，如HTTP、FTP、SMTP等。7层负载均衡器可根据报文内容，配合一定的负载均衡算法来选择后端服务器，即“内容交换器”。如常见的HAProxy、Nginx。</p>
<h3 id="简述LVS、Nginx、HAproxy的什么异同？"><a href="#简述LVS、Nginx、HAproxy的什么异同？" class="headerlink" title="简述LVS、Nginx、HAproxy的什么异同？"></a>简述LVS、Nginx、HAproxy的什么异同？</h3><ul>
<li><p>相同：三者都是软件负载均衡产品。</p>
</li>
<li><p>区别：</p>
</li>
<li><ul>
<li>LVS基于Linux操作系统实现软负载均衡，而HAProxy和Nginx是基于第三方应用实现的软负载均衡；</li>
<li>LVS是可实现4层的IP负载均衡技术，无法实现基于目录、URL的转发。而HAProxy和Nginx都可以实现4层和7层技术，HAProxy可提供TCP和HTTP应用的负载均衡综合解决方案；</li>
<li>LVS因为工作在ISO模型的第四层，其状态监测功能单一，而HAProxy在状监测方面功能更丰富、强大，可支持端口、URL、脚本等多种状态检测方式；</li>
<li>HAProxy功能强大，但整体性能低于4层模式的LVS负载均衡。</li>
<li>Nginx主要用于Web服务器或缓存服务器。</li>
</ul>
</li>
</ul>
<h3 id="简述Heartbeat？"><a href="#简述Heartbeat？" class="headerlink" title="简述Heartbeat？"></a>简述Heartbeat？</h3><p>Heartbeat是Linux-HA项目中的一个组件，它提供了心跳检测和资源接管、集群中服务的监测、失效切换等功能。heartbeat最核心的功能包括两个部分，心跳监测和资源接管。心跳监测可以通过网络链路和串口进行，而且支持冗余链路，它们之间相互发送报文来告诉对方自己当前的状态，如果在指定的时间内未收到对方发送的报文，那么就认为对方失效，这时需启动资源接管模块来接管运行在对方主机上的资源或者服务。</p>
<h3 id="简述Keepalived及其工作原理？"><a href="#简述Keepalived及其工作原理？" class="headerlink" title="简述Keepalived及其工作原理？"></a>简述Keepalived及其工作原理？</h3><p>Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案，可以解决静态路由出现的单点故障问题。</p>
<p>在一个LVS服务集群中通常有主服务器（MASTER）和备份服务器（BACKUP）两种角色的服务器，但是对外表现为一个虚拟IP，主服务器会发送VRRP通告信息给备份服务器，当备份服务器收不到VRRP消息的时候，即主服务器异常的时候，备份服务器就会接管虚拟IP，继续提供服务，从而保证了高可用性。</p>
<h3 id="简述Keepalived体系主要模块及其作用？"><a href="#简述Keepalived体系主要模块及其作用？" class="headerlink" title="简述Keepalived体系主要模块及其作用？"></a>简述Keepalived体系主要模块及其作用？</h3><p>keepalived体系架构中主要有三个模块，分别是core、check和vrrp。</p>
<ul>
<li><code>core模块</code>为keepalived的核心，负责主进程的启动、维护及全局配置文件的加载和解析。</li>
<li><code>vrrp模块</code>是来实现VRRP协议的。</li>
<li><code>check</code>负责健康检查，常见的方式有端口检查及URL检查。</li>
</ul>
<h3 id="简述Keepalived如何通过健康检查来保证高可用？"><a href="#简述Keepalived如何通过健康检查来保证高可用？" class="headerlink" title="简述Keepalived如何通过健康检查来保证高可用？"></a>简述Keepalived如何通过健康检查来保证高可用？</h3><p>Keepalived工作在TCP/IP模型的第三、四和五层，即网络层、传输层和应用层。</p>
<ul>
<li><code>网络层</code>，Keepalived采用ICMP协议向服务器集群中的每个节点发送一个ICMP的数据包，如果某个节点没有返回响应数据包，则认为此节点发生了故障，Keepalived将报告次节点失效，并从服务器集群中剔除故障节点。</li>
<li><code>传输层</code>，Keepalived利用TCP的端口连接和扫描技术来判断集群节点是否正常。如常见的web服务默认端口80，ssh默认端口22等。Keepalived一旦在传输层探测到相应端口没用响应数据返回，则认为此端口发生异常，从而将此端口对应的节点从服务器集群中剔除。</li>
<li><code>应用层</code>，可以运行FTP、telnet、smtp、dns等各种不同类型的高层协议，Keepalived的运行方式也更加全面化和复杂化，用户可以通过自定义Keepalived的工作方式，来设定监测各种程序或服务是否正常，若监测结果与设定的正常结果不一致，将此服务对应的节点从服务器集群中剔除。</li>
</ul>
<p>Keepalived通过完整的健康检查机制，保证集群中的所有节点均有效从而实现高可用。</p>
<h3 id="简述LVS的概念及其作用？"><a href="#简述LVS的概念及其作用？" class="headerlink" title="简述LVS的概念及其作用？"></a>简述LVS的概念及其作用？</h3><p>LVS是linux virtual server的简写linux虚拟服务器，是一个虚拟的服务器集群系统，可以在unix/linux平台下实现负载均衡集群功能。</p>
<p>LVS的主要作用是：通过LVS提供的负载均衡技术实现一个高性能、高可用的服务器群集。因此LVS主要可以实现：</p>
<ul>
<li>把单台计算机无法承受的大规模的并发访问或数据流量分担到多台节点设备上分别处理，减少用户等待响应的时间，提升用户体验。</li>
<li>单个重负载的运算分担到多台节点设备上做并行处理，每个节点设备处理结束后，将结果汇总，返回给用户，系统处理能力得到大幅度提高。</li>
<li>7*24小时的服务保证，任意一个或多个设备节点设备宕机，不能影响到业务。在负载均衡集群中，所有计算机节点都应该提供相同的服务，集群负载均衡获取所有对该服务的如站请求。</li>
</ul>
<h3 id="简述LVS的工作模式及其工作过程？"><a href="#简述LVS的工作模式及其工作过程？" class="headerlink" title="简述LVS的工作模式及其工作过程？"></a>简述LVS的工作模式及其工作过程？</h3><p>LVS 有三种负载均衡的模式，分别是VS/NAT（nat 模式）、VS/DR（路由模式）、VS/TUN（隧道模式）。</p>
<ul>
<li><p>NAT模式（VS-NAT）</p>
</li>
<li><ul>
<li><code>原理</code>：首先负载均衡器接收到客户的请求数据包时，根据调度算法决定将请求发送给哪个后端的真实服务器（RS）。然后负载均衡器就把客户端发送的请求数据包的目标IP地址及端口改成后端真实服务器的IP地址（RIP）。真实服务器响应完请求后，查看默认路由，把响应后的数据包发送给负载均衡器，负载均衡器在接收到响应包后，把包的源地址改成虚拟地址（VIP）然后发送回给客户端。</li>
<li><code>优点</code>：集群中的服务器可以使用任何支持TCP/IP的操作系统，只要负载均衡器有一个合法的IP地址。</li>
<li><code>缺点</code>：扩展性有限，当服务器节点增长过多时，由于所有的请求和应答都需要经过负载均衡器，因此负载均衡器将成为整个系统的瓶颈。</li>
</ul>
</li>
<li><p>IP隧道模式（VS-TUN）</p>
</li>
<li><ul>
<li><code>原理</code>：首先负载均衡器接收到客户的请求数据包时，根据调度算法决定将请求发送给哪个后端的真实服务器（RS）。然后负载均衡器就把客户端发送的请求报文封装一层IP隧道（T-IP）转发到真实服务器（RS）。真实服务器响应完请求后，查看默认路由，把响应后的数据包直接发送给客户端，不需要经过负载均衡器。</li>
<li><code>优点</code>：负载均衡器只负责将请求包分发给后端节点服务器，而RS将应答包直接发给用户。所以，减少了负载均衡器的大量数据流动，负载均衡器不再是系统的瓶颈，也能处理很巨大的请求量。</li>
<li><code>缺点</code>：隧道模式的RS节点需要合法IP，这种方式需要所有的服务器支持“IP Tunneling”。</li>
</ul>
</li>
<li><p>直接路由模式（VS-DR）</p>
</li>
<li><ul>
<li><code>原理</code>：首先负载均衡器接收到客户的请求数据包时，根据调度算法决定将请求发送给哪个后端的真实服务器（RS）。然后负载均衡器就把客户端发送的请求数据包的目标MAC地址改成后端真实服务器的MAC地址（R-MAC）。真实服务器响应完请求后，查看默认路由，把响应后的数据包直接发送给客户端，不需要经过负载均衡器。</li>
<li><code>优点</code>：负载均衡器只负责将请求包分发给后端节点服务器，而RS将应答包直接发给用户。所以，减少了负载均衡器的大量数据流动，负载均衡器不再是系统的瓶颈，也能处理很巨大的请求量。</li>
<li><code>缺点</code>：需要负载均衡器与真实服务器RS都有一块网卡连接到同一物理网段上，必须在同一个局域网环境。</li>
</ul>
</li>
</ul>
<h3 id="简述LVS调度器常见算法（均衡策略）？"><a href="#简述LVS调度器常见算法（均衡策略）？" class="headerlink" title="简述LVS调度器常见算法（均衡策略）？"></a>简述LVS调度器常见算法（均衡策略）？</h3><p>LVS调度器用的调度方法基本分为两类：</p>
<ul>
<li><p>固定调度算法：rr，wrr，dh，sh</p>
</li>
<li><ul>
<li>rr：轮询算法，将请求依次分配给不同的rs节点，即RS节点中均摊分配。适合于RS所有节点处理性能接近的情况。</li>
<li>wrr：加权轮训调度，依据不同RS的权值分配任务。权值较高的RS将优先获得任务，并且分配到的连接数将比权值低的RS更多。相同权值的RS得到相同数目的连接数。</li>
<li>dh：目的地址哈希调度（destination hashing）以目的地址为关键字查找一个静态hash表来获得所需RS。</li>
<li>sh：源地址哈希调度（source hashing）以源地址为关键字查找一个静态hash表来获得需要的RS。</li>
</ul>
</li>
<li><p>动态调度算法：wlc，lc，lblc，lblcr</p>
</li>
<li><ul>
<li>wlc：加权最小连接数调度，假设各台RS的权值依次为Wi，当前tcp连接数依次为Ti，依次去Ti/Wi为最小的RS作为下一个分配的RS。</li>
<li>lc：最小连接数调度（least-connection），IPVS表存储了所有活动的连接。LB会比较将连接请求发送到当前连接最少的RS。</li>
<li>lblc：基于地址的最小连接数调度（locality-based least-connection）：将来自同一个目的地址的请求分配给同一台RS，此时这台服务器是尚未满负荷的。否则就将这个请求分配给连接数最小的RS，并以它作为下一次分配的首先考虑。</li>
</ul>
</li>
</ul>
<h3 id="简述LVS、Nginx、HAProxy各自优缺点？"><a href="#简述LVS、Nginx、HAProxy各自优缺点？" class="headerlink" title="简述LVS、Nginx、HAProxy各自优缺点？"></a>简述LVS、Nginx、HAProxy各自优缺点？</h3><ul>
<li><p>Nginx的优点：</p>
</li>
<li><ul>
<li>工作在网络的7层之上，可以针对http应用做一些分流的策略，比如针对域名、目录结构。Nginx正则规则比HAProxy更为强大和灵活。</li>
<li>Nginx对网络稳定性的依赖非常小，理论上能ping通就就能进行负载功能，LVS对网络稳定性依赖比较大，稳定要求相对更高。</li>
<li>Nginx安装和配置、测试比较简单、方便，有清晰的日志用于排查和管理，LVS的配置、测试就要花比较长的时间了。</li>
<li>可以承担高负载压力且稳定，一般能支撑几万次的并发量，负载度比LVS相对小些。</li>
<li>Nginx可以通过端口检测到服务器内部的故障，比如根据服务器处理网页返回的状态码、超时等等。</li>
<li>Nginx不仅仅是一款优秀的负载均衡器/反向代理软件，它同时也是功能强大的Web应用服务器。</li>
<li>Nginx作为Web反向加速缓存越来越成熟了，速度比传统的Squid服务器更快，很多场景下都将其作为反向代理加速器。</li>
<li>Nginx作为静态网页和图片服务器，这方面的性能非常优秀，同时第三方模块也很多。</li>
</ul>
</li>
<li><p>Nginx的缺点：</p>
</li>
<li><ul>
<li>Nginx仅能支持http、https和Email协议，这样就在适用范围上面小些。</li>
<li>对后端服务器的健康检查，只支持通过端口来检测，不支持通过url来检测。</li>
<li>不支持Session的直接保持，需要通过ip_hash来解决。</li>
</ul>
</li>
<li><p>LVS的优点：</p>
</li>
<li><ul>
<li>抗负载能力强、是工作在网络4层之上仅作分发之用，没有流量的产生。因此负载均衡软件里的性能最强的，对内存和cpu资源消耗比较低。</li>
<li>LVS工作稳定，因为其本身抗负载能力很强，自身有完整的双机热备方案。</li>
<li>无流量，LVS只分发请求，而流量并不从它本身出去，这点保证了均衡器IO的性能不会收到大流量的影响。</li>
<li>应用范围较广，因为LVS工作在4层，所以它几乎可对所有应用做负载均衡，包括http、数据库等。</li>
</ul>
</li>
<li><p>LVS的缺点是：</p>
</li>
<li><ul>
<li>软件本身不支持正则表达式处理，不能做动静分离。相对来说，Nginx/HAProxy+Keepalived则具有明显的优势。</li>
<li>如果是网站应用比较庞大的话，LVS/DR+Keepalived实施起来就比较复杂了。相对来说，Nginx/HAProxy+Keepalived就简单多了。</li>
</ul>
</li>
<li><p>HAProxy的优点：</p>
</li>
<li><ul>
<li>HAProxy也是支持虚拟主机的。</li>
<li>HAProxy的优点能够补充Nginx的一些缺点，比如支持Session的保持，Cookie的引导，同时支持通过获取指定的url来检测后端服务器的状态。</li>
<li>HAProxy跟LVS类似，本身就只是一款负载均衡软件，单纯从效率上来讲HAProxy会比Nginx有更出色的负载均衡速度，在并发处理上也是优于Nginx的。</li>
<li>HAProxy支持TCP协议的负载均衡转发。</li>
</ul>
</li>
</ul>
<h3 id="简述代理服务器的概念及其作用？"><a href="#简述代理服务器的概念及其作用？" class="headerlink" title="简述代理服务器的概念及其作用？"></a>简述代理服务器的概念及其作用？</h3><p>代理服务器是一个位于客户端和原始（资源）服务器之间的服务器，为了从原始服务器取得内容，客户端向代理服务器发送一个请求并指定目标原始服务器，然后代理服务器向原始服务器转交请求并将获得的内容返回给客户端。</p>
<p>其主要作用有：</p>
<ul>
<li>资源获取：代替客户端实现从原始服务器的资源获取；</li>
<li>加速访问：代理服务器可能离原始服务器更近，从而起到一定的加速作用；</li>
<li>缓存作用：代理服务器保存从原始服务器所获取的资源，从而实现客户端快速的获取；</li>
<li>隐藏真实地址：代理服务器代替客户端去获取原始服务器资源，从而隐藏客户端真实信息。</li>
</ul>
<h3 id="简述高可用集群可通过哪两个维度衡量高可用性，各自含义是什么？"><a href="#简述高可用集群可通过哪两个维度衡量高可用性，各自含义是什么？" class="headerlink" title="简述高可用集群可通过哪两个维度衡量高可用性，各自含义是什么？"></a>简述高可用集群可通过哪两个维度衡量高可用性，各自含义是什么？</h3><ul>
<li>RTO（Recovery Time Objective）：RTO指服务恢复的时间，最佳的情况是 0，即服务立即恢复；最坏是无穷大，即服务永远无法恢复；</li>
<li>RPO（Recovery Point Objective）：RPO 指指当灾难发生时允许丢失的数据量，0 意味着使用同步的数据，大于 0 意味着有数据丢失，如“RPO=1 d”指恢复时使用一天前的数据，那么一天之内的数据就丢失了。因此，恢复的最佳情况是 RTO = RPO = 0，几乎无法实现。</li>
</ul>
<h3 id="简述什么是CAP理论？"><a href="#简述什么是CAP理论？" class="headerlink" title="简述什么是CAP理论？"></a>简述什么是CAP理论？</h3><p>CAP理论指出了在分布式系统中需要满足的三个条件，主要包括：</p>
<ul>
<li>Consistency（一致性）：所有节点在同一时间具有相同的数据；</li>
<li>Availability（可用性）：保证每个请求不管成功或者失败都有响应；</li>
<li>Partition tolerance（分区容错性）：系统中任意信息的丢失或失败不影响系统的继续运行。</li>
</ul>
<p>CAP 理论的核心是：一个分布式系统不可能同时很好的满足一致性，可用性和分区容错性这三个需求，最多只能同时较好的满足两个。</p>
<h3 id="简述什么是ACID理论？"><a href="#简述什么是ACID理论？" class="headerlink" title="简述什么是ACID理论？"></a>简述什么是ACID理论？</h3><ul>
<li>原子性(Atomicity)：整体不可分割性，要么全做要不全不做；</li>
<li>一致性(Consistency)：事务执行前、后数据库状态均一致；</li>
<li>隔离性(Isolation)：在事务未提交前，它操作的数据，对其它用户不可见；</li>
<li>持久性 (Durable)：一旦事务成功，将进行永久的变更，记录与redo日志。</li>
</ul>
<h3 id="简述什么是Kubernetes？"><a href="#简述什么是Kubernetes？" class="headerlink" title="简述什么是Kubernetes？"></a>简述什么是Kubernetes？</h3><p>Kubernetes是一个全新的基于容器技术的分布式系统支撑平台。是Google开源的容器集群管理系统（谷歌内部:Borg）。在Docker技术的基础上，为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等一系列完整功能，提高了大规模容器集群管理的便捷性。并且具有完备的集群管理能力，多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和发现机制、內建智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制以及多粒度的资源配额管理能力。</p>
<h3 id="简述Kubernetes和Docker的关系？"><a href="#简述Kubernetes和Docker的关系？" class="headerlink" title="简述Kubernetes和Docker的关系？"></a>简述Kubernetes和Docker的关系？</h3><p>Docker 提供容器的生命周期管理和，Docker 镜像构建运行时容器。它的主要优点是将将软件/应用程序运行所需的设置和依赖项打包到一个容器中，从而实现了可移植性等优点。</p>
<p>Kubernetes 用于关联和编排在多个主机上运行的容器。</p>
<h3 id="简述Kubernetes中什么是Minikube、Kubectl、Kubelet？"><a href="#简述Kubernetes中什么是Minikube、Kubectl、Kubelet？" class="headerlink" title="简述Kubernetes中什么是Minikube、Kubectl、Kubelet？"></a>简述Kubernetes中什么是Minikube、Kubectl、Kubelet？</h3><p><code>Minikube</code> 是一种可以在本地轻松运行一个单节点 Kubernetes 群集的工具。</p>
<p><code>Kubectl</code> 是一个命令行工具，可以使用该工具控制Kubernetes集群管理器，如检查群集资源，创建、删除和更新组件，查看应用程序。</p>
<p><code>Kubelet</code> 是一个代理服务，它在每个节点上运行，并使从服务器与主服务器通信。</p>
<h3 id="简述Kubernetes常见的部署方式？"><a href="#简述Kubernetes常见的部署方式？" class="headerlink" title="简述Kubernetes常见的部署方式？"></a>简述Kubernetes常见的部署方式？</h3><p>常见的Kubernetes部署方式有：</p>
<ul>
<li>kubeadm：也是推荐的一种部署方式；</li>
<li>二进制：</li>
<li>minikube：在本地轻松运行一个单节点 Kubernetes 群集的工具。</li>
</ul>
<h3 id="简述Kubernetes如何实现集群管理？"><a href="#简述Kubernetes如何实现集群管理？" class="headerlink" title="简述Kubernetes如何实现集群管理？"></a>简述Kubernetes如何实现集群管理？</h3><p>在集群管理方面，Kubernetes将集群中的机器划分为一个Master节点和一群工作节点Node。其中，在Master节点运行着集群管理相关的一组进程kube-apiserver、kube-controller-manager和kube-scheduler，这些进程实现了整个集群的资源管理、Pod调度、弹性伸缩、安全控制、系统监控和纠错等管理能力，并且都是全自动完成的。</p>
<h3 id="简述Kubernetes的优势、适应场景及其特点？"><a href="#简述Kubernetes的优势、适应场景及其特点？" class="headerlink" title="简述Kubernetes的优势、适应场景及其特点？"></a>简述Kubernetes的优势、适应场景及其特点？</h3><p>Kubernetes作为一个完备的分布式系统支撑平台，其主要优势：</p>
<ul>
<li>容器编排</li>
<li>轻量级</li>
<li>开源</li>
<li>弹性伸缩</li>
<li>负载均衡</li>
</ul>
<p>Kubernetes常见场景：</p>
<ul>
<li>快速部署应用</li>
<li>快速扩展应用</li>
<li>无缝对接新的应用功能</li>
<li>节省资源，优化硬件资源的使用</li>
</ul>
<p>Kubernetes相关特点：</p>
<ul>
<li>可移植: 支持公有云、私有云、混合云、多重云（multi-cloud）。</li>
<li>可扩展: 模块化,、插件化、可挂载、可组合。</li>
<li>自动化: 自动部署、自动重启、自动复制、自动伸缩/扩展。</li>
</ul>
<h3 id="简述Kubernetes的缺点或当前的不足之处？"><a href="#简述Kubernetes的缺点或当前的不足之处？" class="headerlink" title="简述Kubernetes的缺点或当前的不足之处？"></a>简述Kubernetes的缺点或当前的不足之处？</h3><p>Kubernetes当前存在的缺点（不足）如下：</p>
<ul>
<li>安装过程和配置相对困难复杂。</li>
<li>管理服务相对繁琐。</li>
<li>运行和编译需要很多时间。</li>
<li>它比其他替代品更昂贵。</li>
<li>对于简单的应用程序来说，可能不需要涉及Kubernetes即可满足。</li>
</ul>
<h3 id="简述Kubernetes相关基础概念？"><a href="#简述Kubernetes相关基础概念？" class="headerlink" title="简述Kubernetes相关基础概念？"></a>简述Kubernetes相关基础概念？</h3><ul>
<li><code>master</code>：k8s集群的管理节点，负责管理集群，提供集群的资源数据访问入口。拥有Etcd存储服务（可选），运行Api Server进程，Controller Manager服务进程及Scheduler服务进程。</li>
<li><code>node</code>（worker）：Node（worker）是Kubernetes集群架构中运行Pod的服务节点，是Kubernetes集群操作的单元，用来承载被分配Pod的运行，是Pod运行的宿主机。运行docker eninge服务，守护进程kunelet及负载均衡器kube-proxy。</li>
<li><code>pod</code>：运行于Node节点上，若干相关容器的组合。Pod内包含的容器运行在同一宿主机上，使用相同的网络命名空间、IP地址和端口，能够通过localhost进行通信。Pod是Kurbernetes进行创建、调度和管理的最小单位，它提供了比容器更高层次的抽象，使得部署和管理更加灵活。一个Pod可以包含一个容器或者多个相关容器。</li>
<li><code>label</code>：Kubernetes中的Label实质是一系列的Key/Value键值对，其中key与value可自定义。Label可以附加到各种资源对象上，如Node、Pod、Service、RC等。一个资源对象可以定义任意数量的Label，同一个Label也可以被添加到任意数量的资源对象上去。Kubernetes通过Label Selector（标签选择器）查询和筛选资源对象。</li>
<li><code>Replication Controller</code>：Replication Controller用来管理Pod的副本，保证集群中存在指定数量的Pod副本。集群中副本的数量大于指定数量，则会停止指定数量之外的多余容器数量。反之，则会启动少于指定数量个数的容器，保证数量不变。Replication Controller是实现弹性伸缩、动态扩容和滚动升级的核心。</li>
<li><code>Deployment</code>：Deployment在内部使用了RS来实现目的，Deployment相当于RC的一次升级，其最大的特色为可以随时获知当前Pod的部署进度。</li>
<li><code>HPA</code>（Horizontal Pod Autoscaler）：Pod的横向自动扩容，也是Kubernetes的一种资源，通过追踪分析RC控制的所有Pod目标的负载变化情况，来确定是否需要针对性的调整Pod副本数量。</li>
<li><code>Service</code>：Service定义了Pod的逻辑集合和访问该集合的策略，是真实服务的抽象。Service提供了一个统一的服务访问入口以及服务代理和发现机制，关联多个相同Label的Pod，用户不需要了解后台Pod是如何运行。</li>
<li><code>Volume</code>：Volume是Pod中能够被多个容器访问的共享目录，Kubernetes中的Volume是定义在Pod上，可以被一个或多个Pod中的容器挂载到某个目录下。</li>
<li><code>Namespace</code>：Namespace用于实现多租户的资源隔离，可将集群内部的资源对象分配到不同的Namespace中，形成逻辑上的不同项目、小组或用户组，便于不同的Namespace在共享使用整个集群的资源的同时还能被分别管理。</li>
</ul>
<h3 id="简述Kubernetes集群相关组件？"><a href="#简述Kubernetes集群相关组件？" class="headerlink" title="简述Kubernetes集群相关组件？"></a>简述Kubernetes集群相关组件？</h3><p>Kubernetes Master控制组件，调度管理整个系统（集群），包含如下组件:</p>
<ul>
<li><code>Kubernetes API Server</code>：作为Kubernetes系统的入口，其封装了核心对象的增删改查操作，以RESTful API接口方式提供给外部客户和内部组件调用，集群内各个功能模块之间数据交互和通信的中心枢纽。</li>
<li><code>Kubernetes Scheduler</code>：为新建立的Pod进行节点(node)选择(即分配机器)，负责集群的资源调度。</li>
<li><code>Kubernetes Controller</code>：负责执行各种控制器，目前已经提供了很多控制器来保证Kubernetes的正常运行。</li>
<li><code>Replication Controller</code>：管理维护Replication Controller，关联Replication Controller和Pod，保证Replication Controller定义的副本数量与实际运行Pod数量一致。</li>
<li><code>Node Controller</code>：管理维护Node，定期检查Node的健康状态，标识出(失效|未失效)的Node节点。</li>
<li><code>Namespace Controller</code>：管理维护Namespace，定期清理无效的Namespace，包括Namesapce下的API对象，比如Pod、Service等。</li>
<li><code>Service Controller</code>：管理维护Service，提供负载以及服务代理。</li>
<li><code>EndPoints Controller</code>：管理维护Endpoints，关联Service和Pod，创建Endpoints为Service的后端，当Pod发生变化时，实时更新Endpoints。</li>
<li><code>Service Account Controller</code>：管理维护Service Account，为每个Namespace创建默认的Service Account，同时为Service Account创建Service Account Secret。</li>
<li><code>Persistent Volume Controller</code>：管理维护Persistent Volume和Persistent Volume Claim，为新的Persistent Volume Claim分配Persistent Volume进行绑定，为释放的Persistent Volume执行清理回收。</li>
<li><code>Daemon Set Controller</code>：管理维护Daemon Set，负责创建Daemon Pod，保证指定的Node上正常的运行Daemon Pod。</li>
<li><code>Deployment Controller</code>：管理维护Deployment，关联Deployment和Replication Controller，保证运行指定数量的Pod。当Deployment更新时，控制实现Replication Controller和Pod的更新。</li>
<li><code>Job Controller</code>：管理维护Job，为Jod创建一次性任务Pod，保证完成Job指定完成的任务数目</li>
<li><code>Pod Autoscaler Controller</code>：实现Pod的自动伸缩，定时获取监控数据，进行策略匹配，当满足条件时执行Pod的伸缩动作。</li>
</ul>
<h3 id="简述Kubernetes-RC的机制？"><a href="#简述Kubernetes-RC的机制？" class="headerlink" title="简述Kubernetes RC的机制？"></a>简述Kubernetes RC的机制？</h3><p>Replication Controller用来管理Pod的副本，保证集群中存在指定数量的Pod副本。当定义了RC并提交至Kubernetes集群中之后，Master节点上的Controller Manager组件获悉，并同时巡检系统中当前存活的目标Pod，并确保目标Pod实例的数量刚好等于此RC的期望值，若存在过多的Pod副本在运行，系统会停止一些Pod，反之则自动创建一些Pod。</p>
<h3 id="简述Kubernetes-Replica-Set-和-Replication-Controller-之间有什么区别？"><a href="#简述Kubernetes-Replica-Set-和-Replication-Controller-之间有什么区别？" class="headerlink" title="简述Kubernetes Replica Set 和 Replication Controller 之间有什么区别？"></a>简述Kubernetes Replica Set 和 Replication Controller 之间有什么区别？</h3><p>Replica Set 和 Replication Controller 类似，都是确保在任何给定时间运行指定数量的 Pod 副本。不同之处在于RS 使用基于集合的选择器，而 Replication Controller 使用基于权限的选择器。</p>
<h3 id="简述kube-proxy作用？"><a href="#简述kube-proxy作用？" class="headerlink" title="简述kube-proxy作用？"></a>简述kube-proxy作用？</h3><p>kube-proxy 运行在所有节点上，它监听 apiserver 中 service 和 endpoint 的变化情况，创建路由规则以提供服务 IP 和负载均衡功能。简单理解此进程是Service的透明代理兼负载均衡器，其核心功能是将到某个Service的访问请求转发到后端的多个Pod实例上。</p>
<h3 id="简述kube-proxy-iptables原理？"><a href="#简述kube-proxy-iptables原理？" class="headerlink" title="简述kube-proxy iptables原理？"></a>简述kube-proxy iptables原理？</h3><p>Kubernetes从1.2版本开始，将iptables作为kube-proxy的默认模式。iptables模式下的kube-proxy不再起到Proxy的作用，其核心功能：通过API Server的Watch接口实时跟踪Service与Endpoint的变更信息，并更新对应的iptables规则，Client的请求流量则通过iptables的NAT机制“直接路由”到目标Pod。</p>
<h3 id="简述kube-proxy-ipvs原理？"><a href="#简述kube-proxy-ipvs原理？" class="headerlink" title="简述kube-proxy ipvs原理？"></a>简述kube-proxy ipvs原理？</h3><p>IPVS在Kubernetes1.11中升级为GA稳定版。IPVS则专门用于高性能负载均衡，并使用更高效的数据结构（Hash表），允许几乎无限的规模扩张，因此被kube-proxy采纳为最新模式。</p>
<p>在IPVS模式下，使用iptables的扩展ipset，而不是直接调用iptables来生成规则链。iptables规则链是一个线性的数据结构，ipset则引入了带索引的数据结构，因此当规则很多时，也可以很高效地查找和匹配。</p>
<p>可以将ipset简单理解为一个IP（段）的集合，这个集合的内容可以是IP地址、IP网段、端口等，iptables可以直接添加规则对这个“可变的集合”进行操作，这样做的好处在于可以大大减少iptables规则的数量，从而减少性能损耗。</p>
<h3 id="简述kube-proxy-ipvs和iptables的异同？"><a href="#简述kube-proxy-ipvs和iptables的异同？" class="headerlink" title="简述kube-proxy ipvs和iptables的异同？"></a>简述kube-proxy ipvs和iptables的异同？</h3><p>iptables与IPVS都是基于Netfilter实现的，但因为定位不同，二者有着本质的差别：iptables是为防火墙而设计的；IPVS则专门用于高性能负载均衡，并使用更高效的数据结构（Hash表），允许几乎无限的规模扩张。</p>
<p>与iptables相比，IPVS拥有以下明显优势：</p>
<ul>
<li>1、为大型集群提供了更好的可扩展性和性能；</li>
<li>2、支持比iptables更复杂的复制均衡算法（最小负载、最少连接、加权等）；</li>
<li>3、支持服务器健康检查和连接重试等功能；</li>
<li>4、可以动态修改ipset的集合，即使iptables的规则正在使用这个集合。</li>
</ul>
<h3 id="简述Kubernetes中什么是静态Pod？"><a href="#简述Kubernetes中什么是静态Pod？" class="headerlink" title="简述Kubernetes中什么是静态Pod？"></a>简述Kubernetes中什么是静态Pod？</h3><p>静态pod是由kubelet进行管理的仅存在于特定Node的Pod上，他们不能通过API Server进行管理，无法与ReplicationController、Deployment或者DaemonSet进行关联，并且kubelet无法对他们进行健康检查。静态Pod总是由kubelet进行创建，并且总是在kubelet所在的Node上运行。</p>
<h3 id="简述Kubernetes中Pod可能位于的状态？"><a href="#简述Kubernetes中Pod可能位于的状态？" class="headerlink" title="简述Kubernetes中Pod可能位于的状态？"></a>简述Kubernetes中Pod可能位于的状态？</h3><ul>
<li><code>Pending</code>：API Server已经创建该Pod，且Pod内还有一个或多个容器的镜像没有创建，包括正在下载镜像的过程。</li>
<li><code>Running</code>：Pod内所有容器均已创建，且至少有一个容器处于运行状态、正在启动状态或正在重启状态。</li>
<li><code>Succeeded</code>：Pod内所有容器均成功执行退出，且不会重启。</li>
<li><code>Failed</code>：Pod内所有容器均已退出，但至少有一个容器退出为失败状态。</li>
<li><code>Unknown</code>：由于某种原因无法获取该Pod状态，可能由于网络通信不畅导致。</li>
</ul>
<h3 id="简述Kubernetes创建一个Pod的主要流程？"><a href="#简述Kubernetes创建一个Pod的主要流程？" class="headerlink" title="简述Kubernetes创建一个Pod的主要流程？"></a>简述Kubernetes创建一个Pod的主要流程？</h3><p>Kubernetes中创建一个Pod涉及多个组件之间联动，主要流程如下：</p>
<ul>
<li>1、客户端提交Pod的配置信息（可以是yaml文件定义的信息）到kube-apiserver。</li>
<li>2、Apiserver收到指令后，通知给controller-manager创建一个资源对象。</li>
<li>3、Controller-manager通过api-server将pod的配置信息存储到ETCD数据中心中。</li>
<li>4、Kube-scheduler检测到pod信息会开始调度预选，会先过滤掉不符合Pod资源配置要求的节点，然后开始调度调优，主要是挑选出更适合运行pod的节点，然后将pod的资源配置单发送到node节点上的kubelet组件上。</li>
<li>5、Kubelet根据scheduler发来的资源配置单运行pod，运行成功后，将pod的运行信息返回给scheduler，scheduler将返回的pod运行状况的信息存储到etcd数据中心。</li>
</ul>
<h3 id="简述Kubernetes中Pod的重启策略？"><a href="#简述Kubernetes中Pod的重启策略？" class="headerlink" title="简述Kubernetes中Pod的重启策略？"></a>简述Kubernetes中Pod的重启策略？</h3><p>Pod重启策略（RestartPolicy）应用于Pod内的所有容器，并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某个容器异常退出或者健康检查失败时，kubelet将根据RestartPolicy的设置来进行相应操作。</p>
<p>Pod的重启策略包括Always、OnFailure和Never，默认值为Always。</p>
<ul>
<li><code>Always</code>：当容器失效时，由kubelet自动重启该容器；</li>
<li><code>OnFailure</code>：当容器终止运行且退出码不为0时，由kubelet自动重启该容器；</li>
<li><code>Never</code>：不论容器运行状态如何，kubelet都不会重启该容器。</li>
</ul>
<p>同时Pod的重启策略与控制方式关联，当前可用于管理Pod的控制器包括ReplicationController、Job、DaemonSet及直接管理kubelet管理（静态Pod）。</p>
<p>不同控制器的重启策略限制如下：</p>
<ul>
<li>RC和DaemonSet：必须设置为Always，需要保证该容器持续运行；</li>
<li>Job：OnFailure或Never，确保容器执行完成后不再重启；</li>
<li>kubelet：在Pod失效时重启，不论将RestartPolicy设置为何值，也不会对Pod进行健康检查。</li>
</ul>
<h3 id="简述Kubernetes中Pod的健康检查方式？"><a href="#简述Kubernetes中Pod的健康检查方式？" class="headerlink" title="简述Kubernetes中Pod的健康检查方式？"></a>简述Kubernetes中Pod的健康检查方式？</h3><p>对Pod的健康检查可以通过两类探针来检查：LivenessProbe和ReadinessProbe。</p>
<ul>
<li><code>LivenessProbe探针</code>：用于判断容器是否存活（running状态），如果LivenessProbe探针探测到容器不健康，则kubelet将杀掉该容器，并根据容器的重启策略做相应处理。若一个容器不包含LivenessProbe探针，kubelet认为该容器的LivenessProbe探针返回值用于是“Success”。</li>
<li><code>ReadineeProbe探针</code>：用于判断容器是否启动完成（ready状态）。如果ReadinessProbe探针探测到失败，则Pod的状态将被修改。Endpoint Controller将从Service的Endpoint中删除包含该容器所在Pod的Eenpoint。</li>
<li><code>startupProbe探针</code>：启动检查机制，应用一些启动缓慢的业务，避免业务长时间启动而被上面两类探针kill掉。</li>
</ul>
<h3 id="简述Kubernetes-Pod的LivenessProbe探针的常见方式？"><a href="#简述Kubernetes-Pod的LivenessProbe探针的常见方式？" class="headerlink" title="简述Kubernetes Pod的LivenessProbe探针的常见方式？"></a>简述Kubernetes Pod的LivenessProbe探针的常见方式？</h3><p>kubelet定期执行LivenessProbe探针来诊断容器的健康状态，通常有以下三种方式：</p>
<ul>
<li><code>ExecAction</code>：在容器内执行一个命令，若返回码为0，则表明容器健康。</li>
<li><code>TCPSocketAction</code>：通过容器的IP地址和端口号执行TCP检查，若能建立TCP连接，则表明容器健康。</li>
<li><code>HTTPGetAction</code>：通过容器的IP地址、端口号及路径调用HTTP Get方法，若响应的状态码大于等于200且小于400，则表明容器健康。</li>
</ul>
<h3 id="简述Kubernetes-Pod的常见调度方式？"><a href="#简述Kubernetes-Pod的常见调度方式？" class="headerlink" title="简述Kubernetes Pod的常见调度方式？"></a>简述Kubernetes Pod的常见调度方式？</h3><p>Kubernetes中，Pod通常是容器的载体，主要有如下常见调度方式：</p>
<ul>
<li>Deployment或RC：该调度策略主要功能就是自动部署一个容器应用的多份副本，以及持续监控副本的数量，在集群内始终维持用户指定的副本数量。</li>
<li>NodeSelector：定向调度，当需要手动指定将Pod调度到特定Node上，可以通过Node的标签（Label）和Pod的nodeSelector属性相匹配。</li>
<li>NodeAffinity亲和性调度：亲和性调度机制极大的扩展了Pod的调度能力，目前有两种节点亲和力表达：</li>
<li>requiredDuringSchedulingIgnoredDuringExecution：硬规则，必须满足指定的规则，调度器才可以调度Pod至Node上（类似nodeSelector，语法不同）。</li>
<li>preferredDuringSchedulingIgnoredDuringExecution：软规则，优先调度至满足的Node的节点，但不强求，多个优先级规则还可以设置权重值。</li>
<li>Taints和Tolerations（污点和容忍）：</li>
<li>Taint：使Node拒绝特定Pod运行；</li>
<li>Toleration：为Pod的属性，表示Pod能容忍（运行）标注了Taint的Node。</li>
</ul>
<h3 id="简述Kubernetes初始化容器（init-container）？"><a href="#简述Kubernetes初始化容器（init-container）？" class="headerlink" title="简述Kubernetes初始化容器（init container）？"></a>简述Kubernetes初始化容器（init container）？</h3><p><code>init container</code>的运行方式与应用容器不同，它们必须先于应用容器执行完成，当设置了多个init container时，将按顺序逐个运行，并且只有前一个init container运行成功后才能运行后一个init container。当所有init container都成功运行后，Kubernetes才会初始化Pod的各种信息，并开始创建和运行应用容器。</p>
<h3 id="简述Kubernetes-deployment升级过程？"><a href="#简述Kubernetes-deployment升级过程？" class="headerlink" title="简述Kubernetes deployment升级过程？"></a>简述Kubernetes deployment升级过程？</h3><ul>
<li>初始创建Deployment时，系统创建了一个ReplicaSet，并按用户的需求创建了对应数量的Pod副本。</li>
<li>当更新Deployment时，系统创建了一个新的ReplicaSet，并将其副本数量扩展到1，然后将旧ReplicaSet缩减为2。</li>
<li>之后，系统继续按照相同的更新策略对新旧两个ReplicaSet进行逐个调整。</li>
<li>最后，新的ReplicaSet运行了对应个新版本Pod副本，旧的ReplicaSet副本数量则缩减为0。</li>
</ul>
<h3 id="简述Kubernetes-deployment升级策略？"><a href="#简述Kubernetes-deployment升级策略？" class="headerlink" title="简述Kubernetes deployment升级策略？"></a>简述Kubernetes deployment升级策略？</h3><p>在Deployment的定义中，可以通过spec.strategy指定Pod更新的策略，目前支持两种策略：Recreate（重建）和RollingUpdate（滚动更新），默认值为RollingUpdate。</p>
<ul>
<li><code>Recreate</code>：设置spec.strategy.type=Recreate，表示Deployment在更新Pod时，会先杀掉所有正在运行的Pod，然后创建新的Pod。</li>
<li><code>RollingUpdate</code>：设置spec.strategy.type=RollingUpdate，表示Deployment会以滚动更新的方式来逐个更新Pod。同时，可以通过设置spec.strategy.rollingUpdate下的两个参数（maxUnavailable和maxSurge）来控制滚动更新的过程。</li>
</ul>
<h3 id="简述Kubernetes-DaemonSet类型的资源特性？"><a href="#简述Kubernetes-DaemonSet类型的资源特性？" class="headerlink" title="简述Kubernetes DaemonSet类型的资源特性？"></a>简述Kubernetes DaemonSet类型的资源特性？</h3><p>DaemonSet资源对象会在每个Kubernetes集群中的节点上运行，并且每个节点只能运行一个pod，这是它和deployment资源对象的最大也是唯一的区别。因此，在定义yaml文件中，不支持定义replicas。</p>
<p>它的一般使用场景如下：</p>
<ul>
<li>在去做每个节点的日志收集工作。</li>
<li>监控每个节点的的运行状态。</li>
</ul>
<h3 id="简述Kubernetes自动扩容机制？"><a href="#简述Kubernetes自动扩容机制？" class="headerlink" title="简述Kubernetes自动扩容机制？"></a>简述Kubernetes自动扩容机制？</h3><p>Kubernetes使用Horizontal Pod Autoscaler（HPA）的控制器实现基于CPU使用率进行自动Pod扩缩容的功能。HPA控制器周期性地监测目标Pod的资源性能指标，并与HPA资源对象中的扩缩容条件进行对比，在满足条件时对Pod副本数量进行调整。</p>
<ul>
<li>HPA原理</li>
</ul>
<p>Kubernetes中的某个Metrics Server（Heapster或自定义Metrics Server）持续采集所有Pod副本的指标数据。HPA控制器通过Metrics Server的API（Heapster的API或聚合API）获取这些数据，基于用户定义的扩缩容规则进行计算，得到目标Pod副本数量。</p>
<p>当目标Pod副本数量与当前副本数量不同时，HPA控制器就向Pod的副本控制器（Deployment、RC或ReplicaSet）发起scale操作，调整Pod的副本数量，完成扩缩容操作。</p>
<h3 id="简述Kubernetes-Service类型？"><a href="#简述Kubernetes-Service类型？" class="headerlink" title="简述Kubernetes Service类型？"></a>简述Kubernetes Service类型？</h3><p>通过创建Service，可以为一组具有相同功能的容器应用提供一个统一的入口地址，并且将请求负载分发到后端的各个容器应用上。其主要类型有：</p>
<ul>
<li><code>ClusterIP</code>：虚拟的服务IP地址，该地址用于Kubernetes集群内部的Pod访问，在Node上kube-proxy通过设置的iptables规则进行转发；</li>
<li><code>NodePort</code>：使用宿主机的端口，使能够访问各Node的外部客户端通过Node的IP地址和端口号就能访问服务；</li>
<li><code>LoadBalancer</code>：使用外接负载均衡器完成到服务的负载分发，需要在spec.status.loadBalancer字段指定外部负载均衡器的IP地址，通常用于公有云。</li>
</ul>
<h3 id="简述Kubernetes-Service分发后端的策略？"><a href="#简述Kubernetes-Service分发后端的策略？" class="headerlink" title="简述Kubernetes Service分发后端的策略？"></a>简述Kubernetes Service分发后端的策略？</h3><p>Service负载分发的策略有：RoundRobin和SessionAffinity</p>
<ul>
<li>RoundRobin：默认为轮询模式，即轮询将请求转发到后端的各个Pod上。</li>
<li>SessionAffinity：基于客户端IP地址进行会话保持的模式，即第1次将某个客户端发起的请求转发到后端的某个Pod上，之后从相同的客户端发起的请求都将被转发到后端相同的Pod上。</li>
</ul>
<h3 id="简述Kubernetes-Headless-Service？"><a href="#简述Kubernetes-Headless-Service？" class="headerlink" title="简述Kubernetes Headless Service？"></a>简述Kubernetes Headless Service？</h3><p>在某些应用场景中，若需要人为指定负载均衡器，不使用Service提供的默认负载均衡的功能，或者应用程序希望知道属于同组服务的其他实例。Kubernetes提供了Headless Service来实现这种功能，即不为Service设置ClusterIP（入口IP地址），仅通过Label Selector将后端的Pod列表返回给调用的客户端。</p>
<h3 id="简述Kubernetes外部如何访问集群内的服务？"><a href="#简述Kubernetes外部如何访问集群内的服务？" class="headerlink" title="简述Kubernetes外部如何访问集群内的服务？"></a>简述Kubernetes外部如何访问集群内的服务？</h3><p>对于Kubernetes，集群外的客户端默认情况，无法通过Pod的IP地址或者Service的虚拟IP地址:虚拟端口号进行访问。通常可以通过以下方式进行访问Kubernetes集群内的服务：</p>
<ul>
<li>映射Pod到物理机：将Pod端口号映射到宿主机，即在Pod中采用hostPort方式，以使客户端应用能够通过物理机访问容器应用。</li>
<li>映射Service到物理机：将Service端口号映射到宿主机，即在Service中采用nodePort方式，以使客户端应用能够通过物理机访问容器应用。</li>
<li>映射Sercie到LoadBalancer：通过设置LoadBalancer映射到云服务商提供的LoadBalancer地址。这种用法仅用于在公有云服务提供商的云平台上设置Service的场景。</li>
</ul>
<h3 id="简述Kubernetes-ingress？"><a href="#简述Kubernetes-ingress？" class="headerlink" title="简述Kubernetes ingress？"></a>简述Kubernetes ingress？</h3><p>Kubernetes的Ingress资源对象，用于将不同URL的访问请求转发到后端不同的Service，以实现HTTP层的业务路由机制。</p>
<p>Kubernetes使用了Ingress策略和Ingress Controller，两者结合并实现了一个完整的Ingress负载均衡器。使用Ingress进行负载分发时，Ingress Controller基于Ingress规则将客户端请求直接转发到Service对应的后端Endpoint（Pod）上，从而跳过kube-proxy的转发功能，kube-proxy不再起作用，全过程为：ingress controller + ingress 规则 —-&gt; services。</p>
<p>同时当Ingress Controller提供的是对外服务，则实际上实现的是边缘路由器的功能。</p>
<h3 id="简述Kubernetes镜像的下载策略？"><a href="#简述Kubernetes镜像的下载策略？" class="headerlink" title="简述Kubernetes镜像的下载策略？"></a>简述Kubernetes镜像的下载策略？</h3><p>K8s的镜像下载策略有三种：Always、Never、IFNotPresent。</p>
<ul>
<li><code>Always</code>：镜像标签为latest时，总是从指定的仓库中获取镜像。</li>
<li><code>Never</code>：禁止从仓库中下载镜像，也就是说只能使用本地镜像。</li>
<li><code>IfNotPresent</code>：仅当本地没有对应镜像时，才从目标仓库中下载。默认的镜像下载策略是：当镜像标签是latest时，默认策略是Always；当镜像标签是自定义时（也就是标签不是latest），那么默认策略是IfNotPresent。</li>
</ul>
<h3 id="简述Kubernetes的负载均衡器？"><a href="#简述Kubernetes的负载均衡器？" class="headerlink" title="简述Kubernetes的负载均衡器？"></a>简述Kubernetes的负载均衡器？</h3><p>负载均衡器是暴露服务的最常见和标准方式之一。</p>
<p>根据工作环境使用两种类型的负载均衡器，即内部负载均衡器或外部负载均衡器。内部负载均衡器自动平衡负载并使用所需配置分配容器，而外部负载均衡器将流量从外部负载引导至后端容器。</p>
<h3 id="简述Kubernetes各模块如何与API-Server通信？"><a href="#简述Kubernetes各模块如何与API-Server通信？" class="headerlink" title="简述Kubernetes各模块如何与API Server通信？"></a>简述Kubernetes各模块如何与API Server通信？</h3><p>Kubernetes API Server作为集群的核心，负责集群各功能模块之间的通信。集群内的各个功能模块通过API Server将信息存入etcd，当需要获取和操作这些数据时，则通过API Server提供的REST接口（用GET、LIST或WATCH方法）来实现，从而实现各模块之间的信息交互。</p>
<p>如kubelet进程与API Server的交互：每个Node上的kubelet每隔一个时间周期，就会调用一次API Server的REST接口报告自身状态，API Server在接收到这些信息后，会将节点状态信息更新到etcd中。</p>
<p>如kube-controller-manager进程与API Server的交互：kube-controller-manager中的Node Controller模块通过API Server提供的Watch接口实时监控Node的信息，并做相应处理。</p>
<p>如kube-scheduler进程与API Server的交互：Scheduler通过API Server的Watch接口监听到新建Pod副本的信息后，会检索所有符合该Pod要求的Node列表，开始执行Pod调度逻辑，在调度成功后将Pod绑定到目标节点上。</p>
<h3 id="简述Kubernetes-Scheduler作用及实现原理？"><a href="#简述Kubernetes-Scheduler作用及实现原理？" class="headerlink" title="简述Kubernetes Scheduler作用及实现原理？"></a>简述Kubernetes Scheduler作用及实现原理？</h3><p>Kubernetes Scheduler是负责Pod调度的重要功能模块，Kubernetes Scheduler在整个系统中承担了“承上启下”的重要功能，“承上”是指它负责接收Controller Manager创建的新Pod，为其调度至目标Node；“启下”是指调度完成后，目标Node上的kubelet服务进程接管后继工作，负责Pod接下来生命周期。</p>
<p>Kubernetes Scheduler的作用是将待调度的Pod（API新创建的Pod、Controller Manager为补足副本而创建的Pod等）按照特定的调度算法和调度策略绑定（Binding）到集群中某个合适的Node上，并将绑定信息写入etcd中。</p>
<p>在整个调度过程中涉及三个对象，分别是待调度Pod列表、可用Node列表，以及调度算法和策略。</p>
<p>Kubernetes Scheduler通过调度算法调度为待调度Pod列表中的每个Pod从Node列表中选择一个最适合的Node来实现Pod的调度。随后，目标节点上的kubelet通过API Server监听到Kubernetes Scheduler产生的Pod绑定事件，然后获取对应的Pod清单，下载Image镜像并启动容器。</p>
<h3 id="简述Kubernetes-Scheduler使用哪两种算法将Pod绑定到worker节点？"><a href="#简述Kubernetes-Scheduler使用哪两种算法将Pod绑定到worker节点？" class="headerlink" title="简述Kubernetes Scheduler使用哪两种算法将Pod绑定到worker节点？"></a>简述Kubernetes Scheduler使用哪两种算法将Pod绑定到worker节点？</h3><p>Kubernetes Scheduler根据如下两种调度算法将 Pod 绑定到最合适的工作节点：</p>
<ul>
<li><code>预选</code>（Predicates）：输入是所有节点，输出是满足预选条件的节点。kube-scheduler根据预选策略过滤掉不满足策略的Nodes。如果某节点的资源不足或者不满足预选策略的条件则无法通过预选。如“Node的label必须与Pod的Selector一致”。</li>
<li><code>优选</code>（Priorities）：输入是预选阶段筛选出的节点，优选会根据优先策略为通过预选的Nodes进行打分排名，选择得分最高的Node。例如，资源越富裕、负载越小的Node可能具有越高的排名。</li>
</ul>
<h3 id="简述Kubernetes-kubelet的作用？"><a href="#简述Kubernetes-kubelet的作用？" class="headerlink" title="简述Kubernetes kubelet的作用？"></a>简述Kubernetes kubelet的作用？</h3><p>在Kubernetes集群中，在每个Node（又称Worker）上都会启动一个kubelet服务进程。该进程用于处理Master下发到本节点的任务，管理Pod及Pod中的容器。每个kubelet进程都会在API Server上注册节点自身的信息，定期向Master汇报节点资源的使用情况，并通过cAdvisor监控容器和节点资源。</p>
<h3 id="简述Kubernetes-kubelet监控Worker节点资源是使用什么组件来实现的？"><a href="#简述Kubernetes-kubelet监控Worker节点资源是使用什么组件来实现的？" class="headerlink" title="简述Kubernetes kubelet监控Worker节点资源是使用什么组件来实现的？"></a>简述Kubernetes kubelet监控Worker节点资源是使用什么组件来实现的？</h3><p>kubelet使用cAdvisor对worker节点资源进行监控。在 Kubernetes 系统中，cAdvisor 已被默认集成到 kubelet 组件内，当 kubelet 服务启动时，它会自动启动 cAdvisor 服务，然后 cAdvisor 会实时采集所在节点的性能指标及在节点上运行的容器的性能指标。</p>
<h3 id="简述Kubernetes如何保证集群的安全性？"><a href="#简述Kubernetes如何保证集群的安全性？" class="headerlink" title="简述Kubernetes如何保证集群的安全性？"></a>简述Kubernetes如何保证集群的安全性？</h3><p>Kubernetes通过一系列机制来实现集群的安全控制，主要有如下不同的维度：</p>
<ul>
<li><p>基础设施方面：保证容器与其所在宿主机的隔离；</p>
</li>
<li><p>权限方面：</p>
</li>
<li><ul>
<li>最小权限原则：合理限制所有组件的权限，确保组件只执行它被授权的行为，通过限制单个组件的能力来限制它的权限范围。</li>
<li>用户权限：划分普通用户和管理员的角色。</li>
</ul>
</li>
<li><p>集群方面：</p>
</li>
<li><ul>
<li>API Server的认证授权：Kubernetes集群中所有资源的访问和变更都是通过Kubernetes API Server来实现的，因此需要建议采用更安全的HTTPS或Token来识别和认证客户端身份（Authentication），以及随后访问权限的授权（Authorization）环节。</li>
<li>API Server的授权管理：通过授权策略来决定一个API调用是否合法。对合法用户进行授权并且随后在用户访问时进行鉴权，建议采用更安全的RBAC方式来提升集群安全授权。</li>
<li>敏感数据引入Secret机制：对于集群敏感数据建议使用Secret方式进行保护。</li>
<li>AdmissionControl（准入机制）：对kubernetes api的请求过程中，顺序为：先经过认证 &amp; 授权，然后执行准入操作，最后对目标对象进行操作。</li>
</ul>
</li>
</ul>
<h3 id="简述Kubernetes准入机制？"><a href="#简述Kubernetes准入机制？" class="headerlink" title="简述Kubernetes准入机制？"></a>简述Kubernetes准入机制？</h3><p>在对集群进行请求时，每个准入控制代码都按照一定顺序执行。如果有一个准入控制拒绝了此次请求，那么整个请求的结果将会立即返回，并提示用户相应的error信息。</p>
<p><code>准入控制</code>（AdmissionControl）准入控制本质上为一段准入代码，在对kubernetes api的请求过程中，顺序为：先经过认证 &amp; 授权，然后执行准入操作，最后对目标对象进行操作。常用组件（控制代码）如下：</p>
<ul>
<li><code>AlwaysAdmit</code>：允许所有请求</li>
<li><code>AlwaysDeny</code>：禁止所有请求，多用于测试环境。</li>
<li><code>ServiceAccount</code>：它将serviceAccounts实现了自动化，它会辅助serviceAccount做一些事情，比如如果pod没有serviceAccount属性，它会自动添加一个default，并确保pod的serviceAccount始终存在。</li>
<li><code>LimitRanger</code>：观察所有的请求，确保没有违反已经定义好的约束条件，这些条件定义在namespace中LimitRange对象中。</li>
<li><code>NamespaceExists</code>：观察所有的请求，如果请求尝试创建一个不存在的namespace，则这个请求被拒绝。</li>
</ul>
<h3 id="简述Kubernetes-RBAC及其特点（优势）？"><a href="#简述Kubernetes-RBAC及其特点（优势）？" class="headerlink" title="简述Kubernetes RBAC及其特点（优势）？"></a>简述Kubernetes RBAC及其特点（优势）？</h3><p>RBAC是基于角色的访问控制，是一种基于个人用户的角色来管理对计算机或网络资源的访问的方法。</p>
<p>相对于其他授权模式，RBAC具有如下优势：</p>
<ul>
<li>对集群中的资源和非资源权限均有完整的覆盖。</li>
<li>整个RBAC完全由几个API对象完成， 同其他API对象一样， 可以用kubectl或API进行操作。</li>
<li>可以在运行时进行调整，无须重新启动API Server。</li>
</ul>
<h3 id="简述Kubernetes-Secret作用？"><a href="#简述Kubernetes-Secret作用？" class="headerlink" title="简述Kubernetes Secret作用？"></a>简述Kubernetes Secret作用？</h3><p>Secret对象，主要作用是保管私密数据，比如密码、OAuth Tokens、SSH Keys等信息。将这些私密信息放在Secret对象中比直接放在Pod或Docker Image中更安全，也更便于使用和分发。</p>
<h3 id="简述Kubernetes-Secret有哪些使用方式？"><a href="#简述Kubernetes-Secret有哪些使用方式？" class="headerlink" title="简述Kubernetes Secret有哪些使用方式？"></a>简述Kubernetes Secret有哪些使用方式？</h3><p>创建完secret之后，可通过如下三种方式使用：</p>
<ul>
<li>在创建Pod时，通过为Pod指定Service Account来自动使用该Secret。</li>
<li>通过挂载该Secret到Pod来使用它。</li>
<li>在Docker镜像下载时使用，通过指定Pod的spc.ImagePullSecrets来引用它。</li>
</ul>
<h3 id="简述Kubernetes-PodSecurityPolicy机制？"><a href="#简述Kubernetes-PodSecurityPolicy机制？" class="headerlink" title="简述Kubernetes PodSecurityPolicy机制？"></a>简述Kubernetes PodSecurityPolicy机制？</h3><p>Kubernetes PodSecurityPolicy是为了更精细地控制Pod对资源的使用方式以及提升安全策略。在开启PodSecurityPolicy准入控制器后，Kubernetes默认不允许创建任何Pod，需要创建PodSecurityPolicy策略和相应的RBAC授权策略（Authorizing Policies），Pod才能创建成功。</p>
<h3 id="简述Kubernetes-PodSecurityPolicy机制能实现哪些安全策略？"><a href="#简述Kubernetes-PodSecurityPolicy机制能实现哪些安全策略？" class="headerlink" title="简述Kubernetes PodSecurityPolicy机制能实现哪些安全策略？"></a>简述Kubernetes PodSecurityPolicy机制能实现哪些安全策略？</h3><p>在PodSecurityPolicy对象中可以设置不同字段来控制Pod运行时的各种安全策略，常见的有：</p>
<ul>
<li>特权模式：privileged是否允许Pod以特权模式运行。</li>
<li>宿主机资源：控制Pod对宿主机资源的控制，如hostPID：是否允许Pod共享宿主机的进程空间。</li>
<li>用户和组：设置运行容器的用户ID（范围）或组（范围）。</li>
<li>提升权限：AllowPrivilegeEscalation：设置容器内的子进程是否可以提升权限，通常在设置非root用户（MustRunAsNonRoot）时进行设置。</li>
<li>SELinux：进行SELinux的相关配置。</li>
</ul>
<h3 id="简述Kubernetes网络模型？"><a href="#简述Kubernetes网络模型？" class="headerlink" title="简述Kubernetes网络模型？"></a>简述Kubernetes网络模型？</h3><p>Kubernetes网络模型中每个Pod都拥有一个独立的IP地址，并假定所有Pod都在一个可以直接连通的、扁平的网络空间中。所以不管它们是否运行在同一个Node（宿主机）中，都要求它们可以直接通过对方的IP进行访问。设计这个原则的原因是，用户不需要额外考虑如何建立Pod之间的连接，也不需要考虑如何将容器端口映射到主机端口等问题。</p>
<p>同时为每个Pod都设置一个IP地址的模型使得同一个Pod内的不同容器会共享同一个网络命名空间，也就是同一个Linux网络协议栈。这就意味着同一个Pod内的容器可以通过localhost来连接对方的端口。</p>
<p>在Kubernetes的集群里，IP是以Pod为单位进行分配的。一个Pod内部的所有容器共享一个网络堆栈（相当于一个网络命名空间，它们的IP地址、网络设备、配置等都是共享的）。</p>
<h3 id="简述Kubernetes-CNI模型？"><a href="#简述Kubernetes-CNI模型？" class="headerlink" title="简述Kubernetes CNI模型？"></a>简述Kubernetes CNI模型？</h3><p>CNI提供了一种应用容器的插件化网络解决方案，定义对容器网络进行操作和配置的规范，通过插件的形式对CNI接口进行实现。CNI仅关注在创建容器时分配网络资源，和在销毁容器时删除网络资源。在CNI模型中只涉及两个概念：容器和网络。</p>
<ul>
<li><code>容器</code>（Container）：是拥有独立Linux网络命名空间的环境，例如使用Docker或rkt创建的容器。容器需要拥有自己的Linux网络命名空间，这是加入网络的必要条件。</li>
<li><code>网络</code>（Network）：表示可以互连的一组实体，这些实体拥有各自独立、唯一的IP地址，可以是容器、物理机或者其他网络设备（比如路由器）等。</li>
</ul>
<p>对容器网络的设置和操作都通过插件（Plugin）进行具体实现，CNI插件包括两种类型：CNI Plugin和IPAM（IP Address  Management）Plugin。CNI Plugin负责为容器配置网络资源，IPAM Plugin负责对容器的IP地址进行分配和管理。IPAM Plugin作为CNI Plugin的一部分，与CNI Plugin协同工作。</p>
<h3 id="简述Kubernetes网络策略？"><a href="#简述Kubernetes网络策略？" class="headerlink" title="简述Kubernetes网络策略？"></a>简述Kubernetes网络策略？</h3><p>为实现细粒度的容器间网络访问隔离策略，Kubernetes引入Network Policy。</p>
<p>Network Policy的主要功能是对Pod间的网络通信进行限制和准入控制，设置允许访问或禁止访问的客户端Pod列表。Network Policy定义网络策略，配合策略控制器（Policy Controller）进行策略的实现。</p>
<h3 id="简述Kubernetes网络策略原理？"><a href="#简述Kubernetes网络策略原理？" class="headerlink" title="简述Kubernetes网络策略原理？"></a>简述Kubernetes网络策略原理？</h3><p>Network Policy的工作原理主要为：policy controller需要实现一个API Listener，监听用户设置的Network Policy定义，并将网络访问规则通过各Node的Agent进行实际设置（Agent则需要通过CNI网络插件实现）。</p>
<h3 id="简述Kubernetes中flannel的作用？"><a href="#简述Kubernetes中flannel的作用？" class="headerlink" title="简述Kubernetes中flannel的作用？"></a>简述Kubernetes中flannel的作用？</h3><p>Flannel可以用于Kubernetes底层网络的实现，主要作用有：</p>
<ul>
<li>它能协助Kubernetes，给每一个Node上的Docker容器都分配互相不冲突的IP地址。</li>
<li>它能在这些IP地址之间建立一个覆盖网络（Overlay Network），通过这个覆盖网络，将数据包原封不动地传递到目标容器内。</li>
</ul>
<h3 id="简述Kubernetes-Calico网络组件实现原理？"><a href="#简述Kubernetes-Calico网络组件实现原理？" class="headerlink" title="简述Kubernetes Calico网络组件实现原理？"></a>简述Kubernetes Calico网络组件实现原理？</h3><p>Calico是一个基于BGP的纯三层的网络方案，与OpenStack、Kubernetes、AWS、GCE等云平台都能够良好地集成。</p>
<p>Calico在每个计算节点都利用Linux Kernel实现了一个高效的vRouter来负责数据转发。每个vRouter都通过BGP协议把在本节点上运行的容器的路由信息向整个Calico网络广播，并自动设置到达其他节点的路由转发规则。</p>
<p>Calico保证所有容器之间的数据流量都是通过IP路由的方式完成互联互通的。Calico节点组网时可以直接利用数据中心的网络结构（L2或者L3），不需要额外的NAT、隧道或者Overlay Network，没有额外的封包解包，能够节约CPU运算，提高网络效率。</p>
<h3 id="简述Kubernetes共享存储的作用？"><a href="#简述Kubernetes共享存储的作用？" class="headerlink" title="简述Kubernetes共享存储的作用？"></a>简述Kubernetes共享存储的作用？</h3><p>Kubernetes对于有状态的容器应用或者对数据需要持久化的应用，因此需要更加可靠的存储来保存应用产生的重要数据，以便容器应用在重建之后仍然可以使用之前的数据。因此需要使用共享存储。</p>
<h3 id="简述Kubernetes数据持久化的方式有哪些？"><a href="#简述Kubernetes数据持久化的方式有哪些？" class="headerlink" title="简述Kubernetes数据持久化的方式有哪些？"></a>简述Kubernetes数据持久化的方式有哪些？</h3><p>Kubernetes通过数据持久化来持久化保存重要数据，常见的方式有：</p>
<ul>
<li><p>EmptyDir（空目录）：没有指定要挂载宿主机上的某个目录，直接由Pod内保部映射到宿主机上。类似于docker中的manager volume。</p>
</li>
<li><p>场景：</p>
</li>
<li><ul>
<li>只需要临时将数据保存在磁盘上，比如在合并/排序算法中；</li>
<li>作为两个容器的共享存储。</li>
</ul>
</li>
<li><p>特性：</p>
</li>
<li><ul>
<li>同个pod里面的不同容器，共享同一个持久化目录，当pod节点删除时，volume的数据也会被删除。</li>
<li>emptyDir的数据持久化的生命周期和使用的pod一致，一般是作为临时存储使用。</li>
</ul>
</li>
<li><p>Hostpath：将宿主机上已存在的目录或文件挂载到容器内部。类似于docker中的bind mount挂载方式。</p>
</li>
<li><ul>
<li>特性：增加了pod与节点之间的耦合。</li>
</ul>
</li>
</ul>
<p>PersistentVolume（简称PV）：如基于NFS服务的PV，也可以基于GFS的PV。它的作用是统一数据持久化目录，方便管理。</p>
<h3 id="简述Kubernetes-PV和PVC？"><a href="#简述Kubernetes-PV和PVC？" class="headerlink" title="简述Kubernetes PV和PVC？"></a>简述Kubernetes PV和PVC？</h3><p>PV是对底层网络共享存储的抽象，将共享存储定义为一种“资源”。</p>
<p>PVC则是用户对存储资源的一个“申请”。</p>
<h3 id="简述Kubernetes-PV生命周期内的阶段？"><a href="#简述Kubernetes-PV生命周期内的阶段？" class="headerlink" title="简述Kubernetes PV生命周期内的阶段？"></a>简述Kubernetes PV生命周期内的阶段？</h3><p>某个PV在生命周期中可能处于以下4个阶段（Phaes）之一。</p>
<ul>
<li>Available：可用状态，还未与某个PVC绑定。</li>
<li>Bound：已与某个PVC绑定。</li>
<li>Released：绑定的PVC已经删除，资源已释放，但没有被集群回收。</li>
<li>Failed：自动资源回收失败。</li>
</ul>
<h3 id="简述Kubernetes所支持的存储供应模式？"><a href="#简述Kubernetes所支持的存储供应模式？" class="headerlink" title="简述Kubernetes所支持的存储供应模式？"></a>简述Kubernetes所支持的存储供应模式？</h3><p>Kubernetes支持两种资源的存储供应模式：静态模式（Static）和动态模式（Dynamic）。</p>
<ul>
<li><code>静态模式</code>：集群管理员手工创建许多PV，在定义PV时需要将后端存储的特性进行设置。</li>
<li><code>动态模式</code>：集群管理员无须手工创建PV，而是通过StorageClass的设置对后端存储进行描述，标记为某种类型。此时要求PVC对存储的类型进行声明，系统将自动完成PV的创建及与PVC的绑定。</li>
</ul>
<h3 id="简述Kubernetes-CSI模型？"><a href="#简述Kubernetes-CSI模型？" class="headerlink" title="简述Kubernetes CSI模型？"></a>简述Kubernetes CSI模型？</h3><p>Kubernetes CSI是Kubernetes推出与容器对接的存储接口标准，存储提供方只需要基于标准接口进行存储插件的实现，就能使用Kubernetes的原生存储机制为容器提供存储服务。CSI使得存储提供方的代码能和Kubernetes代码彻底解耦，部署也与Kubernetes核心组件分离，显然，存储插件的开发由提供方自行维护，就能为Kubernetes用户提供更多的存储功能，也更加安全可靠。</p>
<p>CSI包括CSI Controller和CSI Node：</p>
<ul>
<li>CSI Controller的主要功能是提供存储服务视角对存储资源和存储卷进行管理和操作。</li>
<li>CSI Node的主要功能是对主机（Node）上的Volume进行管理和操作。</li>
</ul>
<h3 id="简述Kubernetes-Worker节点加入集群的过程？"><a href="#简述Kubernetes-Worker节点加入集群的过程？" class="headerlink" title="简述Kubernetes Worker节点加入集群的过程？"></a>简述Kubernetes Worker节点加入集群的过程？</h3><p>通常需要对Worker节点进行扩容，从而将应用系统进行水平扩展。主要过程如下：</p>
<ul>
<li>1、在该Node上安装Docker、kubelet和kube-proxy服务；</li>
<li>2、然后配置kubelet和kubeproxy的启动参数，将Master URL指定为当前Kubernetes集群Master的地址，最后启动这些服务；</li>
<li>3、通过kubelet默认的自动注册机制，新的Worker将会自动加入现有的Kubernetes集群中；</li>
<li>4、Kubernetes Master在接受了新Worker的注册之后，会自动将其纳入当前集群的调度范围。</li>
</ul>
<h3 id="简述Kubernetes-Pod如何实现对节点的资源控制？"><a href="#简述Kubernetes-Pod如何实现对节点的资源控制？" class="headerlink" title="简述Kubernetes Pod如何实现对节点的资源控制？"></a>简述Kubernetes Pod如何实现对节点的资源控制？</h3><p>Kubernetes集群里的节点提供的资源主要是计算资源，计算资源是可计量的能被申请、分配和使用的基础资源。当前Kubernetes集群中的计算资源主要包括CPU、GPU及Memory。CPU与Memory是被Pod使用的，因此在配置Pod时可以通过参数CPU Request及Memory Request为其中的每个容器指定所需使用的CPU与Memory量，Kubernetes会根据Request的值去查找有足够资源的Node来调度此Pod。</p>
<p>通常，一个程序所使用的CPU与Memory是一个动态的量，确切地说，是一个范围，跟它的负载密切相关：负载增加时，CPU和Memory的使用量也会增加。</p>
<h3 id="简述Kubernetes-Requests和Limits如何影响Pod的调度？"><a href="#简述Kubernetes-Requests和Limits如何影响Pod的调度？" class="headerlink" title="简述Kubernetes Requests和Limits如何影响Pod的调度？"></a>简述Kubernetes Requests和Limits如何影响Pod的调度？</h3><p>当一个Pod创建成功时，Kubernetes调度器（Scheduler）会为该Pod选择一个节点来执行。对于每种计算资源（CPU和Memory）而言，每个节点都有一个能用于运行Pod的最大容量值。调度器在调度时，首先要确保调度后该节点上所有Pod的CPU和内存的Requests总和，不超过该节点能提供给Pod使用的CPU和Memory的最大容量值。</p>
<h3 id="简述Kubernetes-Metric-Service？"><a href="#简述Kubernetes-Metric-Service？" class="headerlink" title="简述Kubernetes Metric Service？"></a>简述Kubernetes Metric Service？</h3><p>在Kubernetes从1.10版本后采用Metrics Server作为默认的性能数据采集和监控，主要用于提供核心指标（Core Metrics），包括Node、Pod的CPU和内存使用指标。</p>
<p>对其他自定义指标（Custom Metrics）的监控则由Prometheus等组件来完成。</p>
<h3 id="简述Kubernetes中，如何使用EFK实现日志的统一管理？"><a href="#简述Kubernetes中，如何使用EFK实现日志的统一管理？" class="headerlink" title="简述Kubernetes中，如何使用EFK实现日志的统一管理？"></a>简述Kubernetes中，如何使用EFK实现日志的统一管理？</h3><p>在Kubernetes集群环境中，通常一个完整的应用或服务涉及组件过多，建议对日志系统进行集中化管理，通常采用EFK实现。</p>
<p>EFK是 Elasticsearch、Fluentd 和 Kibana 的组合，其各组件功能如下：</p>
<ul>
<li>Elasticsearch：是一个搜索引擎，负责存储日志并提供查询接口；</li>
<li>Fluentd：负责从 Kubernetes 搜集日志，每个node节点上面的fluentd监控并收集该节点上面的系统日志，并将处理过后的日志信息发送给Elasticsearch；</li>
<li>Kibana：提供了一个 Web GUI，用户可以浏览和搜索存储在 Elasticsearch 中的日志。</li>
</ul>
<p>通过在每台node上部署一个以DaemonSet方式运行的fluentd来收集每台node上的日志。Fluentd将docker日志目录/var/lib/docker/containers和/var/log目录挂载到Pod中，然后Pod会在node节点的/var/log/pods目录中创建新的目录，可以区别不同的容器日志输出，该目录下有一个日志文件链接到/var/lib/docker/contianers目录下的容器日志输出。</p>
<h3 id="简述Kubernetes如何进行优雅的节点关机维护？"><a href="#简述Kubernetes如何进行优雅的节点关机维护？" class="headerlink" title="简述Kubernetes如何进行优雅的节点关机维护？"></a>简述Kubernetes如何进行优雅的节点关机维护？</h3><p>由于Kubernetes节点运行大量Pod，因此在进行关机维护之前，建议先使用kubectl drain将该节点的Pod进行驱逐，然后进行关机维护。</p>
<h3 id="简述Kubernetes集群联邦？"><a href="#简述Kubernetes集群联邦？" class="headerlink" title="简述Kubernetes集群联邦？"></a>简述Kubernetes集群联邦？</h3><p>Kubernetes集群联邦可以将多个Kubernetes集群作为一个集群进行管理。因此，可以在一个数据中心/云中创建多个Kubernetes集群，并使用集群联邦在一个地方控制/管理所有集群。</p>
<h3 id="简述Helm及其优势？"><a href="#简述Helm及其优势？" class="headerlink" title="简述Helm及其优势？"></a>简述Helm及其优势？</h3><p><code>Helm</code> 是 Kubernetes 的软件包管理工具。类似 Ubuntu 中使用的apt、Centos中使用的yum 或者Python中的 pip 一样。</p>
<p>Helm能够将一组K8S资源打包统一管理, 是查找、共享和使用为Kubernetes构建的软件的最佳方式。</p>
<p>Helm中通常每个包称为一个Chart，一个Chart是一个目录（一般情况下会将目录进行打包压缩，形成name-version.tgz格式的单一文件，方便传输和存储）。</p>
<ul>
<li>Helm优势</li>
</ul>
<p>在 Kubernetes中部署一个可以使用的应用，需要涉及到很多的 Kubernetes 资源的共同协作。使用helm则具有如下优势：</p>
<ul>
<li>统一管理、配置和更新这些分散的 k8s 的应用资源文件；</li>
<li>分发和复用一套应用模板；</li>
<li>将应用的一系列资源当做一个软件包管理。</li>
<li>对于应用发布者而言，可以通过 Helm 打包应用、管理应用依赖关系、管理应用版本并发布应用到软件仓库。</li>
<li>对于使用者而言，使用 Helm 后不用需要编写复杂的应用部署文件，可以以简单的方式在 Kubernetes 上查找、安装、升级、回滚、卸载应用程序。</li>
</ul>
<h3 id="简述OpenShift及其特性？"><a href="#简述OpenShift及其特性？" class="headerlink" title="简述OpenShift及其特性？"></a>简述OpenShift及其特性？</h3><p>OpenShift是一个容器应用程序平台，用于在安全的、可伸缩的资源上部署新应用程序，而配置和管理开销最小。</p>
<p>OpenShift构建于Red Hat Enterprise Linux、Docker和Kubernetes之上，为企业级应用程序提供了一个安全且可伸缩的多租户操作系统，同时还提供了集成的应用程序运行时和库。</p>
<p>其主要特性：</p>
<ul>
<li>自助服务平台：OpenShift允许开发人员使用Source-to-Image(S2I)从模板或自己的源代码管理存储库创建应用程序。系统管理员可以为用户和项目定义资源配额和限制，以控制系统资源的使用。</li>
<li>多语言支持：OpenShift支持Java、Node.js、PHP、Perl以及直接来自Red Hat的Ruby。OpenShift还支持中间件产品，如Apache httpd、Apache Tomcat、JBoss EAP、ActiveMQ和Fuse。</li>
<li>自动化：OpenShift提供应用程序生命周期管理功能，当上游源或容器映像发生更改时，可以自动重新构建和重新部署容器。根据调度和策略扩展或故障转移应用程序。</li>
<li>用户界面：OpenShift提供用于部署和监视应用程序的web UI，以及用于远程管理应用程序和资源的CLi。</li>
<li>协作：OpenShift允许在组织内或与更大的社区共享项目。</li>
<li>可伸缩性和高可用性：OpenShift提供了容器多租户和一个分布式应用程序平台，其中包括弹性，高可用性，以便应用程序能够在物理机器宕机等事件中存活下来。OpenShift提供了对容器健康状况的自动发现和自动重新部署。</li>
<li>容器可移植性：在OpenShift中，应用程序和服务使用标准容器映像进行打包，组合应用程序使用Kubernetes进行管理。这些映像可以部署到基于这些基础技术的其他平台上。</li>
<li>开源：没有厂商锁定。</li>
<li>安全性：OpenShift使用SELinux提供多层安全性、基于角色的访问控制以及与外部身份验证系统(如LDAP和OAuth)集成的能力。</li>
<li>动态存储管理：OpenShift使用Kubernetes持久卷和持久卷声明的方式为容器数据提供静态和动态存储管理</li>
<li>基于云(或不基于云)：可以在裸机服务器、活来自多个供应商的hypervisor和大多数IaaS云提供商上部署OpenShift容器平台。</li>
<li>企业级：Red Hat支持OpenShift、选定的容器映像和应用程序运行时。可信的第三方容器映像、运行时和应用程序由Red Hat认证。可以在OpenShift提供的高可用性的强化安全环境中运行内部或第三方应用程序。</li>
<li>日志聚合和metrics：可以在中心节点收集、聚合和分析部署在OpenShift上的应用程序的日志信息。OpenShift能够实时收集关于应用程序的度量和运行时信息，并帮助不断优化性能。</li>
<li>其他特性：OpenShift支持微服务体系结构，OpenShift的本地特性足以支持DevOps流程，很容易与标准和定制的持续集成/持续部署工具集成。</li>
</ul>
<h3 id="简述OpenShift-projects及其作用？"><a href="#简述OpenShift-projects及其作用？" class="headerlink" title="简述OpenShift projects及其作用？"></a>简述OpenShift projects及其作用？</h3><p>OpenShift管理projects和users。一个projects对Kubernetes资源进行分组，以便用户可以使用访问权限。还可以为projects分配配额，从而限制了已定义的pod、volumes、services和其他资源。</p>
<p>project允许一组用户独立于其他组组织和管理其内容，必须允许用户访问项目。如果允许创建项目，用户将自动访问自己的项目。</p>
<h3 id="简述OpenShift高可用的实现？"><a href="#简述OpenShift高可用的实现？" class="headerlink" title="简述OpenShift高可用的实现？"></a>简述OpenShift高可用的实现？</h3><p>OpenShift平台集群的高可用性(HA)有两个不同的方面：</p>
<p>OpenShift基础设施本身的HA(即主机)；</p>
<p>以及在OpenShift集群中运行的应用程序的HA。</p>
<p>默认情况下，OpenShift为master节点提供了完全支持的本机HA机制。对于应用程序或“pods”，如果pod因任何原因丢失，Kubernetes将调度另一个副本，将其连接到服务层和持久存储。如果整个节点丢失，Kubernetes会为它所有的pod安排替换节点，最终所有的应用程序都会重新可用。pod中的应用程序负责它们自己的状态，因此它们需要自己维护应用程序状态(如HTTP会话复制或数据库复制)。</p>
<h3 id="简述OpenShift的SDN网络实现？"><a href="#简述OpenShift的SDN网络实现？" class="headerlink" title="简述OpenShift的SDN网络实现？"></a>简述OpenShift的SDN网络实现？</h3><p>默认情况下，Docker网络使用仅使用主机虚机网桥bridge，主机内的所有容器都连接至该网桥。连接到此桥的所有容器都可以彼此通信，但不能与不同主机上的容器通信。</p>
<p>为了支持跨集群的容器之间的通信，OpenShift容器平台使用了软件定义的网络(SDN)方法。软件定义的网络是一种网络模型，它通过几个网络层的抽象来管理网络服务。SDN将处理流量的软件(称为控制平面)和路由流量的底层机制(称为数据平面)解耦。SDN支持控制平面和数据平面之间的通信。</p>
<p>在OpenShift中，可以为pod网络配置三个SDN插件:</p>
<ul>
<li>1、ovs-subnet：默认插件，子网提供了一个flat pod网络，其中每个pod可以与其他pod和service通信。</li>
<li>2、ovs-multitenant：该为pod和服务提供了额外的隔离层。当使用此插件时，每个project接收一个惟一的虚拟网络ID (VNID)，该ID标识来自属于该project的pod的流量。通过使用VNID，来自不同project的pod不能与其他project的pod和service通信。</li>
<li>3、ovs-network policy：此插件允许管理员使用NetworkPolicy对象定义自己的隔离策略。</li>
</ul>
<p>cluster network由OpenShift SDN建立和维护，它使用Open vSwitch创建overlay网络，master节点不能通过集群网络访问容器，除非master同时也为node节点。</p>
<h3 id="简述OpenShift角色及其作用？"><a href="#简述OpenShift角色及其作用？" class="headerlink" title="简述OpenShift角色及其作用？"></a>简述OpenShift角色及其作用？</h3><p>OpenShift的角色具有不同级别的访问和策略，包括集群和本地策略。user和group可以同时与多个role关联。</p>
<h3 id="简述OpenShift支持哪些身份验证？"><a href="#简述OpenShift支持哪些身份验证？" class="headerlink" title="简述OpenShift支持哪些身份验证？"></a>简述OpenShift支持哪些身份验证？</h3><p>OpenShift容器平台支持的其他认证类型包括:</p>
<ul>
<li>Basic Authentication (Remote)：一种通用的后端集成机制，允许用户使用针对远程标识提供者验证的凭据登录到OpenShift容器平台。用户将他们的用户名和密码发送到OpenShift容器平台，OpenShift平台通过到服务器的请求验证这些凭据，并将凭据作为基本的Auth头传递。这要求用户在登录过程中向OpenShift容器平台输入他们的凭据。</li>
<li>Request Header Authentication：用户使用请求头值(如X-RemoteUser)登录到OpenShift容器平台。它通常与身份验证代理结合使用，身份验证代理对用户进行身份验证，然后通过请求头值为OpenShift容器平台提供用户标识。</li>
<li>Keystone Authentication：Keystone是一个OpenStack项目，提供标识、令牌、目录和策略服务。OpenShift容器平台与Keystone集成，通过配置OpenStack Keystone v3服务器将用户存储在内部数据库中，从而支持共享身份验证。这种配置允许用户使用Keystone凭证登录OpenShift容器平台。</li>
<li>LDAP Authentication：用户使用他们的LDAP凭证登录到OpenShift容器平台。在身份验证期间，LDAP目录将搜索与提供的用户名匹配的条目。如果找到匹配项，则尝试使用条目的专有名称(DN)和提供的密码进行简单绑定。</li>
<li>GitHub Authentication：GitHub使用OAuth，它允许与OpenShift容器平台集成使用OAuth身份验证来促进令牌交换流。这允许用户使用他们的GitHub凭证登录到OpenShift容器平台。为了防止使用GitHub用户id的未授权用户登录到OpenShift容器平台集群，可以将访问权限限制在特定的GitHub组织中。</li>
</ul>
<h3 id="简述什么是中间件？"><a href="#简述什么是中间件？" class="headerlink" title="简述什么是中间件？"></a>简述什么是中间件？</h3><p><code>中间件</code>是一种独立的系统软件或服务程序，分布式应用软件借助这种软件在不同的技术之间共享资源。通常位于客户机/服务器的操作系统中间，是连接两个独立应用程序或独立系统的软件。</p>
<p>通过中间件实现两个不同系统之间的信息交换。</p>
<ul>
<li>作者：木二</li>
<li>链接：<a target="_blank" rel="noopener" href="https://www.yuque.com/docs/share/d3dd1e8e-6828-4da7-9e30-6a4f45c6fa8e">https://www.yuque.com/docs/share/d3dd1e8e-6828-4da7-9e30-6a4f45c6fa8e</a></li>
</ul>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/interview/" rel="tag">interview</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-k8s/Ceph 基础篇 - 集群部署及故障排查"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/k8s/Ceph%20%E5%9F%BA%E7%A1%80%E7%AF%87%20-%20%E9%9B%86%E7%BE%A4%E9%83%A8%E7%BD%B2%E5%8F%8A%E6%95%85%E9%9A%9C%E6%8E%92%E6%9F%A5/"
    >Ceph 基础篇 - 集群部署及故障排查.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/k8s/Ceph%20%E5%9F%BA%E7%A1%80%E7%AF%87%20-%20%E9%9B%86%E7%BE%A4%E9%83%A8%E7%BD%B2%E5%8F%8A%E6%95%85%E9%9A%9C%E6%8E%92%E6%9F%A5/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/k8s/">k8s</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="Ceph-基础篇-集群部署及故障排查"><a href="#Ceph-基础篇-集群部署及故障排查" class="headerlink" title="Ceph 基础篇 - 集群部署及故障排查"></a>Ceph 基础篇 - 集群部署及故障排查</h1><p><strong>部署之前</strong>  </p>
<hr>
<p><strong>安装方式</strong>  </p>
<p>ceph-deploy 安装，官网已经没有这个部署页面，N之前的版本可以使用，包括N，自动化安装工具，后面的版本将不支持，这里我们选择使用ceph-deploy安装；  </p>
<p>cephadm 安装，近期出现的安装方式，需要 Centos8 的环境，并且支持图形化安装或者命令行安装，O版本之后，未来推荐使用 cephadm 安装；  </p>
<p>手动安装，一步步的教你如何安装，这种方法可以清晰了解部署细节，以及 Ceph 集群各组件的关联关系等；  </p>
<p>Rook 安装，与现有 kubernetes 集群集成安装，安装到集群中；</p>
<p>ceph-ansiable 自动化安装；  </p>
<p><strong>服务器规划</strong></p>
<table height="101"><tbody><tr height="19"><td x:str="" height="14" width="84" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;font-weight: 700;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(84, 130, 53);">服务器IP</td><td x:str="" height="14" width="104" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;font-weight: 700;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(84, 130, 53);">主机名</td><td x:str="" height="14" width="218" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;font-weight: 700;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(84, 130, 53);">承担功能</td><td x:str="" height="14" width="89" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;font-weight: 700;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(84, 130, 53);">系统版本</td></tr><tr height="44"><td x:str="" height="33" width="82" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">100.73.18.152<br>(复用节点)</td><td x:str="" height="33" width="104" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);">ceph-node01</td><td x:str="" height="33" width="218" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);word-break: break-all;">ceph-deploy、ceph-admin<br>client(复用)<br>mon、mgr、rgw、mds、osd</td><td rowspan="3" x:str="" height="61" width="89" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;text-align: left;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);">Centos&nbsp;7.2&nbsp;3.10.0-693.5.2.el7.x86_64</td></tr><tr height="19"><td x:str="" height="14" width="84" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">100.73.18.153</td><td x:str="" height="14" width="104" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);">ceph-node02</td><td x:str="" height="14" width="218" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);">mon、mgr、rgw、mds、osd</td></tr><tr height="19"><td x:str="" height="14" width="84" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">100.73.18.128</td><td x:str="" height="14" width="104" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);">ceph-node03</td><td x:str="" height="14" width="218" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);word-break: break-all;">mon、mgr、mds、osd</td></tr></tbody></table>



<p><strong>架构图</strong></p>
<p><img src=""></p>
<p>（架构图）  </p>
<p><strong>Ceph 概念介绍</strong></p>
<table height="472"><tbody><tr height="19"><td x:str="" height="14" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;font-weight: 700;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(84, 130, 53);">组件</td><td x:str="" height="14" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;font-weight: 700;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(84, 130, 53);">功能&nbsp;</td></tr><tr height="104"><td x:str="" height="78" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">Monitor</td><td x:str="" height="78" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);">它是一个在主机上面运行的守护进程，这个守护进程，扮演着监控集群整个组件的职责，包括多少个存储池，存储池中有多少个PG，PG与osd映射的关系，每个节点有多少个osd等等，都要监视，它是整个集群运行图（Cluster Map）的持有者，一共5个运行图。<br>它还维护集群的认证信息，客户端有用户名密码等；内部的OSD也需要与mon通信，也需要认证，它是通过cephX协议进行认证的，各个组件之间通信，都必须认证，认证都需要经过mon,因此mon还是认证中心，有些同学就会想到，如果集群很大了的话，认证中心会不会成为瓶颈，所以这也是需要多台mon的原因，mon在认证上是无状态的，可以做任意横向扩展，可以使用负载均衡负载的；</td></tr><tr height="56"><td x:str="" height="42" width="53" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">Managers</td><td x:str="" height="42" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);word-break: break-all;">它的专门守护进程是ceph-mgr，负责收集集群的状态指标，运行时的metrics，存储的利用率，当前性能指标和系统负载，它有很多基于Python的插件，以实现ceph-mgr功能的提升，用户辅助mon的。</td></tr><tr height="72"><td x:str="" height="54" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">OSD</td><td x:str="" height="54" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);word-break: break-all;">Osd是指的单独的资源存储设备，一般情况下一台服务器上面放多块磁盘，ceph为了使用每个节点上面的osd单独进行管理，每一个osd都会有一个单独的、专用的守护进程，比如说，一台服务器上面有6个osd，就需要6个ceph-osd进程，每一个磁盘上面，都有一个守护进程。它提供存储 PG 的数据、数据复制、恢复、再均衡，并提供一些监控信息供mon和mgr来check，并且还可以通过心跳检测其它副本；至少需要有三个osd，不是三台节点哦，这里要注意下，一般情况下我们是1主2从，1主PG、2个副本PG、以确保其高可用性；</td></tr><tr height="19"><td x:str="" height="14" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">CURSH</td><td x:str="" height="14" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);">CRUSH 是 Ceph 使用的数据分布算法，类似一致性哈希，让数据分配到预期的位置。</td></tr><tr height="29"><td x:str="" height="21" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">PG</td><td x:str="" height="21" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);">PG 全称 Placement Groups，是一个逻辑的概念，一个 PG 包含多个 OSD 。引入 PG 这一层其实是为了更好的分配数据和定位数据。</td></tr><tr height="29"><td x:str="" height="21" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">Object</td><td x:str="" height="21" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);">文件需要切分成大小为4M（默认）的块即对象，Ceph 最底层的存储单元就是 Object对象，每个 Object 包含元数据和原始数据；</td></tr><tr height="19"><td x:str="" height="14" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">RADOS</td><td x:str="" height="14" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);">实现数据分配、Failover 等集群操作。</td></tr><tr height="29"><td x:str="" height="21" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">Libradio</td><td x:str="" height="21" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);">&nbsp;librados提供了访问RADOS存储集群支持异步通信的API接口，支持对集群中对象数据的直接并行访问，用户可通过支持的编程语言开发自定义客户端程序通过RADOS协议与存储系统进行交互；</td></tr><tr height="19"><td x:str="" height="14" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">MDS</td><td x:str="" height="14" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);">MDS全称Ceph Metadata Server，是CephFS服务依赖的元数据服务；</td></tr><tr height="19"><td x:str="" height="14" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">RBD</td><td x:str="" height="14" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);">RBD全称 RADOS Block Device，是 Ceph 对外提供的块设备服务；</td></tr><tr height="29"><td x:str="" height="21" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">RGW</td><td x:str="" height="21" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);">RGW依赖于在RADOS集群基础上独立运行的守护进程(ceph-radosgw)基于http 或https协议提供相关的API服务，不过，通常仅在需要以REST对象形式存取数据时才部署RGW；</td></tr><tr height="29"><td x:str="" height="21" width="56" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;border-width: 0.5pt;border-color: rgb(0, 0, 0);background: rgb(255, 217, 102);">CephFS</td><td x:str="" height="21" width="482" style="color: rgb(0, 0, 0);font-size: 10pt;font-family: 宋体;vertical-align: middle;white-space: normal;border-width: 0.5pt;border-color: rgb(0, 0, 0);word-break: break-all;">CephFS全称Ceph File System，是 Ceph 对外提供的文件系统服务，它是最早出现的，但也是最后可用于生产的，目前的版本，可以用在生产中。</td></tr></tbody></table>

<p>**<br>安装部署**</p>
<hr>
<p><strong>1. 修改主机名以及添加host（所有机器）</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"># 修改主机名  </span><br><span class="line">hostnamectl set-hostname ceph-node01  </span><br><span class="line">  </span><br><span class="line"># 修改hosts  </span><br><span class="line">[root@ceph-node01 ~]# cat &#x2F;etc&#x2F;hosts  </span><br><span class="line">127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4  </span><br><span class="line">::1 localhost localhost.localdomain localhost6 localhost6.localdomain6  </span><br><span class="line">100.73.18.152 ceph-node01  </span><br><span class="line">100.73.18.153 ceph-node02  </span><br><span class="line">100.73.18.128 ceph-node03  </span><br><span class="line">[root@ceph-node01 ~]#</span><br></pre></td></tr></table></figure>

<p><strong>2. ceph-admin 节点与其它节点做信任</strong></p>
<p>由于ceph-deploy命令不支持运行中输入密码，因此必须在管理节点（ceph-admin）上生成 ssh 密钥并将其分发到ceph 集群的各个节点上面。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># 生成密钥  </span><br><span class="line">ssh-keygen -t rsa -P &quot;&quot;  </span><br><span class="line">  </span><br><span class="line"># copy 密钥  </span><br><span class="line">ssh-copy-id -i .ssh&#x2F;id_rsa.pub &lt;node-name&gt;</span><br></pre></td></tr></table></figure>

<p><strong>3.  安装 NTP 服务，并做时间同步</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum -y install ntp</span><br></pre></td></tr></table></figure>

<p>安装完成后，配置/etc/ntp.conf 即可，如果公司有 NTP server，直接配置即可，如果没有，可以选择一个公网的；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ~]# ntpq -p  </span><br><span class="line">     remote refid st t when poll reach delay offset jitter  </span><br><span class="line">&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;  </span><br><span class="line">*100.100.1.2 202.28.93.5 2 u 665 1024 377 1.268 -7.338 1.523  </span><br><span class="line">-100.100.1.2 202.28.116.236 2 u 1015 1024 377 0.805 -12.547 0.693  </span><br><span class="line">+100.100.1.3 203.159.70.33 2 u 117 1024 377 0.742 -5.007 1.814  </span><br><span class="line">+100.100.1.4 203.159.70.33 2 u 19 1024 377 0.731 -5.770 2.652  </span><br><span class="line">[root@ceph-node01 ~]#</span><br></pre></td></tr></table></figure>

<p>通过以上命令测试ntp 是否配置好即可，可以配置一台，其它节点服务器指向这一台即可；   </p>
<p><strong>4.  关闭 iptables 或 firewalld 服务 （也可以指定端口通信，不关闭）</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">systemctl stop firewalld.service  </span><br><span class="line">systemctl stop iptables.service  </span><br><span class="line">systemctl disable firewalld.service  </span><br><span class="line">systemctl disable iptables.service</span><br></pre></td></tr></table></figure>

<p><strong>5. 关闭并禁用 SELinux</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># 修改  </span><br><span class="line">sed -i &#39;s@^\(SELINUX&#x3D;\).*@\1disabled@&#39; &#x2F;etc&#x2F;sysconfig&#x2F;selinux  </span><br><span class="line">  </span><br><span class="line"># 生效  </span><br><span class="line">setenforce 0  </span><br><span class="line">  </span><br><span class="line"># 查看  </span><br><span class="line">getenforce</span><br></pre></td></tr></table></figure>

<p><strong>6.  配置 yum 源（同步到所有机器）</strong></p>
<p>删除原来的配置文件，从阿里源下载最新yum 文件；阿里云的镜像官网：<a target="_blank" rel="noopener" href="https://developer.aliyun.com/mirror/%EF%BC%8C%E5%9F%BA%E6%9C%AC%E4%B8%8A%E5%8F%AF%E4%BB%A5%E5%9C%A8%E8%BF%99%E9%87%8C%E6%89%BE%E5%88%B0%E6%89%80%E6%9C%89%E7%9B%B8%E5%85%B3%E7%9A%84%E9%95%9C%E5%83%8F%E9%93%BE%E6%8E%A5%EF%BC%9B">https://developer.aliyun.com/mirror/，基本上可以在这里找到所有相关的镜像链接；</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">wget -O &#x2F;etc&#x2F;yum.repos.d&#x2F;CentOS-Base.repo https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;repo&#x2F;Centos-7.repo  </span><br><span class="line">wget -O &#x2F;etc&#x2F;yum.repos.d&#x2F;epel.repo http:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;repo&#x2F;epel-7.repo</span><br></pre></td></tr></table></figure>

<p>注意 epel-7 也要下载一下，默认的ceph版本较低，配置 ceph 源，需要自己根据阿里提供的进行自己编写，如下：  </p>
<p>ceph：<a target="_blank" rel="noopener" href="https://mirrors.aliyun.com/ceph/?spm=a2c6h.13651104.0.0.435f22d16X5Jk7">https://mirrors.aliyun.com/ceph/?spm=a2c6h.13651104.0.0.435f22d16X5Jk7</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 yum.repos.d]# cat ceph.repo  </span><br><span class="line">[norch]  </span><br><span class="line">name&#x3D;norch  </span><br><span class="line">baseurl&#x3D;https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;ceph&#x2F;rpm-nautilus&#x2F;el7&#x2F;noarch&#x2F;  </span><br><span class="line">enabled&#x3D;1  </span><br><span class="line">gpgcheck&#x3D;0  </span><br><span class="line">  </span><br><span class="line">[x86_64]  </span><br><span class="line">name&#x3D;x86_64  </span><br><span class="line">baseurl&#x3D;https:&#x2F;&#x2F;mirrors.aliyun.com&#x2F;ceph&#x2F;rpm-nautilus&#x2F;el7&#x2F;x86_64&#x2F;  </span><br><span class="line">enabled&#x3D;1  </span><br><span class="line">gpgcheck&#x3D;0  </span><br><span class="line">[root@ceph-node01 yum.repos.d]#</span><br></pre></td></tr></table></figure>

<p>然后同步repo到所有机器上面；    </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># 查看当前 yum 源  </span><br><span class="line">yum repolist  </span><br><span class="line">yum repolist all</span><br></pre></td></tr></table></figure>

<p>就是把服务器的包信息下载到本地电脑缓存起来，makecache建立一个缓存，以后用yum install时就在缓存中搜索，提高了速度，配合yum -C search xxx使用。</p>
<p>yum makecache<br>yum -C search xxx<br>yum clean all</p>
<p><strong>7. 管理节点安装 ceph-deploy</strong></p>
<p>Ceph 存储集群部署过程中可通过管理节点使用ceph-deploy全程进行，这里首先在管理节点安装ceph-deploy及其依赖的程序包，这里要注意安装 python-setuptools 工具包；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install ceph-deploy python-setuptools python2-subprocess32</span><br></pre></td></tr></table></figure>

<p><strong>8. 部署RADOS存储集群</strong></p>
<p>创建一个专属目录；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir ceph-deploy &amp;&amp; cd ceph-deploy</span><br></pre></td></tr></table></figure>

<p>初始化第一个MON节点，准备创建集群</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ceph-deploy new --cluster-network 100.73.18.0&#x2F;24 --public-network 100.73.18.0&#x2F;24 &lt;node-name&gt;</span><br></pre></td></tr></table></figure>

<p>--cluster-network  内部数据同步使用；</p>
<p>--public-network 对外提供服务使用的；</p>
<p>生成三个配置文件，ceph.conf（配置文件）、ceph-deploy-ceph.log（日志文件）、 ceph.mon.keyring（认证文件）。</p>
<p><strong>9. 安装 ceph 集群</strong></p>
<p>ceph-deploy install {ceph-node} {….}</p>
<p>这里使用这种方式安装的话，会自动化的把软件安装包安装上去，这种安装方式不太好，因为它会重新配置yum源，包括我们的 epel yum源，还有 ceph 的 yum 源，都会指向他内置的yum源，这样会导致你访问到国外，下载很慢，建议手动安装，下面每台机器都手动安装即可，如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# yum -y install ceph ceph-mds ceph-mgr ceph-osd ceph-radosgw ceph-mon</span><br></pre></td></tr></table></figure>



<p><strong>10. 配置文件和admin密钥copy到ceph集群各节点</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph -s  </span><br><span class="line">[errno 2] error connecting to the cluster  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#  </span><br><span class="line"># 原因，没有admin 文件，下面通过 admin 命令进行 copy  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy admin ceph-node01 ceph-node02 ceph-node03</span><br></pre></td></tr></table></figure>



<p>再次查看集群状态如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph -s  </span><br><span class="line">  cluster:  </span><br><span class="line">    id: cc10b0cb-476f-420c-b1d6-e48c1dc929af  </span><br><span class="line">    health: HEALTH_OK  </span><br><span class="line">  </span><br><span class="line">  services:  </span><br><span class="line">    mon: 1 daemons, quorum ceph-node01 (age 2m)  </span><br><span class="line">    mgr: no daemons active  </span><br><span class="line">    osd: 0 osds: 0 up, 0 in  </span><br><span class="line">  </span><br><span class="line">  data:  </span><br><span class="line">    pools: 0 pools, 0 pgs  </span><br><span class="line">    objects: 0 objects, 0 B  </span><br><span class="line">    usage: 0 B used, 0 B &#x2F; 0 B avail  </span><br><span class="line">    pgs:  </span><br><span class="line">  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>

<p>发现 services 只有一个 mon，没有 mgr、也没有 osd；  </p>
<p><strong>11. 安装 mgr</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy mgr create ceph-node01  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph -s  </span><br><span class="line">  cluster:  </span><br><span class="line">    id: cc10b0cb-476f-420c-b1d6-e48c1dc929af  </span><br><span class="line">    health: HEALTH_WARN  </span><br><span class="line">            OSD count 0 &lt; osd_pool_default_size 3  </span><br><span class="line">  </span><br><span class="line">  services:  </span><br><span class="line">    mon: 1 daemons, quorum ceph-node01 (age 4m)  </span><br><span class="line">    mgr: ceph-node01(active, since 84s)  </span><br><span class="line">    osd: 0 osds: 0 up, 0 in  </span><br><span class="line">  </span><br><span class="line">  data:  </span><br><span class="line">    pools: 0 pools, 0 pgs  </span><br><span class="line">    objects: 0 objects, 0 B  </span><br><span class="line">    usage: 0 B used, 0 B &#x2F; 0 B avail  </span><br><span class="line">    pgs:  </span><br><span class="line">  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p><strong>12. 向 RADOS 集群添加 OSD</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy osd list ceph-node01</span><br></pre></td></tr></table></figure>

<p>ceph-deploy disk 命令可以检查并列出 OSD 节点上所有可用的磁盘的相关信息；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy disk zap ceph-node01 &#x2F;dev&#x2F;vdb</span><br></pre></td></tr></table></figure>

<p>在管理节点上使用 ceph-deploy 命令擦除计划专用于 OSD 磁盘上的所有分区表和数据以便用于 OSD，命令格式 为 ceph-deploy disk zap {osd-server-name} {disk-name}，需要注意的是此步会清除目标设备上的所有数据。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"># 查看磁盘情况  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# lsblk  </span><br><span class="line">NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT  </span><br><span class="line">sr0 11:0 1 1024M 0 rom  </span><br><span class="line">vda 252:0 0 50G 0 disk  </span><br><span class="line">├─vda1 252:1 0 500M 0 part &#x2F;boot  </span><br><span class="line">└─vda2 252:2 0 49.5G 0 part  </span><br><span class="line">  ├─centos-root 253:0 0 44.5G 0 lvm &#x2F;  </span><br><span class="line">  └─centos-swap 253:1 0 5G 0 lvm [SWAP]  </span><br><span class="line">vdb 252:16 0 100G 0 disk  </span><br><span class="line">vdc 252:32 0 100G 0 disk  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#  </span><br><span class="line"># 添加 osd  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy osd create ceph-node01 --data &#x2F;dev&#x2F;vdb  </span><br><span class="line">。。。  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy osd create ceph-node02 --data &#x2F;dev&#x2F;vdb  </span><br><span class="line">。。。  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy osd create ceph-node03 --data &#x2F;dev&#x2F;vdb  </span><br><span class="line">。。。</span><br></pre></td></tr></table></figure>



<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy osd list ceph-node01</span><br></pre></td></tr></table></figure>

<p>ceph-deploy osd list 命令列出指定节点上的 OSD 信息；</p>
<p><strong>13.  查看 OSD</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph osd tree  </span><br><span class="line">ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRI-AFF  </span><br><span class="line">-1 0.39067 root default  </span><br><span class="line">-3 0.09769 host ceph-node01  </span><br><span class="line"> 0 hdd 0.09769 osd.0 up 1.00000 1.00000  </span><br><span class="line">-5 0.09769 host ceph-node02  </span><br><span class="line"> 1 hdd 0.09769 osd.1 up 1.00000 1.00000  </span><br><span class="line">-7 0.19530 host ceph-node03  </span><br><span class="line"> 2 hdd 0.19530 osd.2 up 1.00000 1.00000  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph osd stat  </span><br><span class="line">3 osds: 3 up (since 2d), 3 in (since 2d); epoch: e26  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph osd ls  </span><br><span class="line">0  </span><br><span class="line">1  </span><br><span class="line">2  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph osd dump  </span><br><span class="line">epoch 26  </span><br><span class="line">fsid cc10b0cb-476f-420c-b1d6-e48c1dc929af  </span><br><span class="line">created 2020-09-29 09:14:30.781641  </span><br><span class="line">modified 2020-09-29 10:14:06.100849  </span><br><span class="line">flags sortbitwise,recovery_deletes,purged_snapdirs,pglog_hardlimit  </span><br><span class="line">crush_version 7  </span><br><span class="line">full_ratio 0.95  </span><br><span class="line">backfillfull_ratio 0.9  </span><br><span class="line">nearfull_ratio 0.85  </span><br><span class="line">require_min_compat_client jewel  </span><br><span class="line">min_compat_client jewel  </span><br><span class="line">require_osd_release nautilus  </span><br><span class="line">pool 1 &#39;ceph-demo&#39; replicated size 2 min_size 1 crush_rule 0 object_hash rjenkins pg_num 128 pgp_num 128 autoscale_mode warn last_change 25 lfor 0&#x2F;0&#x2F;20 flags hashpspool,selfmanaged_snaps stripe_width 0  </span><br><span class="line">  removed_snaps [1~3]  </span><br><span class="line">max_osd 3  </span><br><span class="line">osd.0 up in weight 1 up_from 5 up_thru 22 down_at 0 last_clean_interval [0,0) [v2:100.73.18.152:6802&#x2F;11943,v1:100.73.18.152:6803&#x2F;11943] [v2:100.73.18.152:6804&#x2F;11943,v1:100.73.18.152:6805&#x2F;11943] exists,up 136f6cf7-05a0-4325-aa92-ad316560edff  </span><br><span class="line">osd.1 up in weight 1 up_from 9 up_thru 22 down_at 0 last_clean_interval [0,0) [v2:100.73.18.153:6800&#x2F;10633,v1:100.73.18.153:6801&#x2F;10633] [v2:100.73.18.153:6802&#x2F;10633,v1:100.73.18.153:6803&#x2F;10633] exists,up 79804c00-2662-47a1-9987-95579afa10b6  </span><br><span class="line">osd.2 up in weight 1 up_from 13 up_thru 22 down_at 0 last_clean_interval [0,0) [v2:100.73.18.128:6800&#x2F;10558,v1:100.73.18.128:6801&#x2F;10558] [v2:100.73.18.128:6802&#x2F;10558,v1:100.73.18.128:6803&#x2F;10558] exists,up f15cacec-fdcd-4d3c-8bb8-ab3565cb4d0b  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>

<p>查看 OSD 的相关信息；</p>
<p> <strong>14. 扩展 mon</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy mon add ceph-node02  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy mon add ceph-node03</span><br></pre></td></tr></table></figure>



<p>由于 mon 需要使用 paxos 算法进行选举一个 leader，可以查看选举状态；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph quorum_status</span><br></pre></td></tr></table></figure>



<p>查看 mon 状态</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph mon stat  </span><br><span class="line">e3: 3 mons at &#123;ceph-node01&#x3D;[v2:100.73.18.152:3300&#x2F;0,v1:100.73.18.152:6789&#x2F;0],ceph-node02&#x3D;[v2:100.73.18.153:3300&#x2F;0,v1:100.73.18.153:6789&#x2F;0],ceph-node03&#x3D;[v2:100.73.18.128:3300&#x2F;0,v1:100.73.18.128:6789&#x2F;0]&#125;, election epoch 12, leader 0 ceph-node01, quorum 0,1,2 ceph-node01,ceph-node02,ceph-node03  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p>查看 mon 详情</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph mon dump  </span><br><span class="line">dumped monmap epoch 3  </span><br><span class="line">epoch 3  </span><br><span class="line">fsid cc10b0cb-476f-420c-b1d6-e48c1dc929af  </span><br><span class="line">last_changed 2020-09-29 09:28:35.692432  </span><br><span class="line">created 2020-09-29 09:14:30.493476  </span><br><span class="line">min_mon_release 14 (nautilus)  </span><br><span class="line">0: [v2:100.73.18.152:3300&#x2F;0,v1:100.73.18.152:6789&#x2F;0] mon.ceph-node01  </span><br><span class="line">1: [v2:100.73.18.153:3300&#x2F;0,v1:100.73.18.153:6789&#x2F;0] mon.ceph-node02  </span><br><span class="line">2: [v2:100.73.18.128:3300&#x2F;0,v1:100.73.18.128:6789&#x2F;0] mon.ceph-node03  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p><strong>15. 扩展 mgr</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy mgr create ceph-node02 ceph-node03</span><br></pre></td></tr></table></figure>



<p><strong>16. 查看集群状态</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph -s  </span><br><span class="line">  cluster:  </span><br><span class="line">    id: cc10b0cb-476f-420c-b1d6-e48c1dc929af  </span><br><span class="line">    health: HEALTH_OK  </span><br><span class="line">  </span><br><span class="line">  services:  </span><br><span class="line">    mon: 3 daemons, quorum ceph-node01,ceph-node02,ceph-node03 (age 9m)  </span><br><span class="line">    mgr: ceph-node01(active, since 20m), standbys: ceph-node02, ceph-node03  </span><br><span class="line">    osd: 3 osds: 3 up (since 13m), 3 in (since 13m)  </span><br><span class="line">  </span><br><span class="line">  data:  </span><br><span class="line">    pools: 0 pools, 0 pgs  </span><br><span class="line">    objects: 0 objects, 0 B  </span><br><span class="line">    usage: 3.0 GiB used, 397 GiB &#x2F; 400 GiB avail  </span><br><span class="line">    pgs:  </span><br><span class="line">  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>

<p>3个mon、3个mgr、3个osd的RADOS集群创建成功；  </p>
<p><strong>17. 移出故障 OSD</strong></p>
<p>Ceph集群中的一个OSD通常对应于一个设备，且运行于专用的守护进程。在某OSD设备出现故障，或管理员出于管 理之需确实要移除特定的OSD设备时，需要先停止相关的守护进程，而后再进行移除操作。</p>
<p>1. 停用设备:ceph osd out {osd-num}</p>
<p>2. 停止进程:sudo systemctl stop ceph-osd@{osd-num} </p>
<p>3. 移除设备:ceph osd purge {id} –yes-i-really-mean-it</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph osd out 0  </span><br><span class="line">marked out osd.0.  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# systemctl stop ceph-osd@0  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph osd purge 0 --yes-i-really-mean-it  </span><br><span class="line">purged osd.0  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p>移出后查看状态  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph -s  </span><br><span class="line">  cluster:  </span><br><span class="line">    id: cc10b0cb-476f-420c-b1d6-e48c1dc929af  </span><br><span class="line">    health: HEALTH_WARN  </span><br><span class="line">            2 daemons have recently crashed  </span><br><span class="line">            OSD count 2 &lt; osd_pool_default_size 3  </span><br><span class="line">  </span><br><span class="line">  services:  </span><br><span class="line">    mon: 3 daemons, quorum ceph-node01,ceph-node02,ceph-node03 (age 15h)  </span><br><span class="line">    mgr: ceph-node01(active, since 15h)  </span><br><span class="line">    osd: 2 osds: 2 up (since 37h), 2 in (since 37h)  </span><br><span class="line">  </span><br><span class="line">  data:  </span><br><span class="line">    pools: 1 pools, 128 pgs  </span><br><span class="line">    objects: 54 objects, 137 MiB  </span><br><span class="line">    usage: 2.3 GiB used, 298 GiB &#x2F; 300 GiB avail  </span><br><span class="line">    pgs: 128 active+clean  </span><br><span class="line">  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p>磁盘擦除数据时报错</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy disk zap ceph-node01 &#x2F;dev&#x2F;vdb  </span><br><span class="line">。。。  </span><br><span class="line">[ceph_deploy][ERROR ] RuntimeError: Failed to execute command: &#x2F;usr&#x2F;sbin&#x2F;ceph-volume lvm zap &#x2F;dev&#x2F;vdb</span><br></pre></td></tr></table></figure>



<p>根据擦除数据时出现报错后，可以使用dd清空数据，然后再重启</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# dd if&#x3D;&#x2F;dev&#x2F;zero of&#x3D;&#x2F;dev&#x2F;vdb bs&#x3D;512K count&#x3D;1  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# reboot</span><br></pre></td></tr></table></figure>



<p>再次进行zap 擦除数据，使磁盘可做 OSD 加入集群</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy disk zap ceph-node01 &#x2F;dev&#x2F;vdb  </span><br><span class="line">。。。。  </span><br><span class="line"> &#x2F;usr&#x2F;sbin&#x2F;ceph-volume lvm zap &#x2F;dev&#x2F;vdb  </span><br><span class="line">[ceph-node01][WARNIN] --&gt; Zapping: &#x2F;dev&#x2F;vdb  </span><br><span class="line">[ceph-node01][WARNIN] --&gt; --destroy was not specified, but zapping a whole device will remove the partition table  </span><br><span class="line">[ceph-node01][WARNIN] Running command: &#x2F;usr&#x2F;bin&#x2F;dd if&#x3D;&#x2F;dev&#x2F;zero of&#x3D;&#x2F;dev&#x2F;vdb bs&#x3D;1M count&#x3D;10 conv&#x3D;fsync  </span><br><span class="line">[ceph-node01][WARNIN] stderr: 记录了10+0 的读入  </span><br><span class="line">[ceph-node01][WARNIN] 记录了10+0 的写出  </span><br><span class="line">[ceph-node01][WARNIN] 10485760字节(10 MB)已复制  </span><br><span class="line">[ceph-node01][WARNIN] stderr: ，0.0398864 秒，263 MB&#x2F;秒  </span><br><span class="line">[ceph-node01][WARNIN] --&gt; Zapping successful for: &lt;Raw Device: &#x2F;dev&#x2F;vdb&gt;  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p>磁盘修复好后，再加入集群</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy osd create ceph-node01 --data &#x2F;dev&#x2F;vdb  </span><br><span class="line">。。。  </span><br><span class="line">[ceph-node01][WARNIN] Running command: &#x2F;usr&#x2F;bin&#x2F;systemctl enable --runtime ceph-osd@0  </span><br><span class="line">[ceph-node01][WARNIN] stderr: Created symlink from &#x2F;run&#x2F;systemd&#x2F;system&#x2F;ceph-osd.target.wants&#x2F;ceph-osd@0.service to &#x2F;usr&#x2F;lib&#x2F;systemd&#x2F;system&#x2F;ceph-osd@.service.  </span><br><span class="line">[ceph-node01][WARNIN] Running command: &#x2F;usr&#x2F;bin&#x2F;systemctl start ceph-osd@0  </span><br><span class="line">[ceph-node01][WARNIN] --&gt; ceph-volume lvm activate successful for osd ID: 0  </span><br><span class="line">[ceph-node01][WARNIN] --&gt; ceph-volume lvm create successful for: &#x2F;dev&#x2F;vdb  </span><br><span class="line">[ceph-node01][INFO ] checking OSD status...  </span><br><span class="line">[ceph-node01][DEBUG ] find the location of an executable  </span><br><span class="line">[ceph-node01][INFO ] Running command: &#x2F;bin&#x2F;ceph --cluster&#x3D;ceph osd stat --format&#x3D;json  </span><br><span class="line">[ceph_deploy.osd][DEBUG ] Host ceph-node01 is now ready for osd use.  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p>查看集群状态，发现正在迁移数据  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph -s  </span><br><span class="line">  cluster:  </span><br><span class="line">    id: cc10b0cb-476f-420c-b1d6-e48c1dc929af  </span><br><span class="line">    health: HEALTH_WARN  </span><br><span class="line">            Degraded data redundancy: 9&#x2F;88 objects degraded (10.227%), 7 pgs degraded  </span><br><span class="line">            2 daemons have recently crashed  </span><br><span class="line">  </span><br><span class="line">  services:  </span><br><span class="line">    mon: 3 daemons, quorum ceph-node01,ceph-node02,ceph-node03 (age 15h)  </span><br><span class="line">    mgr: ceph-node01(active, since 15h)  </span><br><span class="line">    osd: 3 osds: 3 up (since 6s), 3 in (since 6s)  </span><br><span class="line">  </span><br><span class="line">  data:  </span><br><span class="line">    pools: 1 pools, 128 pgs  </span><br><span class="line">    objects: 44 objects, 105 MiB  </span><br><span class="line">    usage: 3.3 GiB used, 397 GiB &#x2F; 400 GiB avail  </span><br><span class="line">    pgs: 24.219% pgs not active  </span><br><span class="line">             9&#x2F;88 objects degraded (10.227%)  </span><br><span class="line">             1&#x2F;88 objects misplaced (1.136%)  </span><br><span class="line">             90 active+clean  </span><br><span class="line">             31 peering  </span><br><span class="line">             6 active+recovery_wait+degraded  </span><br><span class="line">             1 active+recovering+degraded  </span><br><span class="line">  </span><br><span class="line">  io:  </span><br><span class="line">    recovery: 1.3 MiB&#x2F;s, 1 keys&#x2F;s, 1 objects&#x2F;s  </span><br><span class="line">  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p>等待一段时间后，发现数据迁移完成，但有两个进程crashed了  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph -s  </span><br><span class="line">  cluster:  </span><br><span class="line">    id: cc10b0cb-476f-420c-b1d6-e48c1dc929af  </span><br><span class="line">    health: HEALTH_WARN  </span><br><span class="line">            2 daemons have recently crashed  </span><br><span class="line">  </span><br><span class="line">  services:  </span><br><span class="line">    mon: 3 daemons, quorum ceph-node01,ceph-node02,ceph-node03 (age 15h)  </span><br><span class="line">    mgr: ceph-node01(active, since 15h), standbys: ceph-node02, ceph-node03  </span><br><span class="line">    osd: 3 osds: 3 up (since 30m), 3 in (since 30m)  </span><br><span class="line">  </span><br><span class="line">  data:  </span><br><span class="line">    pools: 1 pools, 128 pgs  </span><br><span class="line">    objects: 54 objects, 137 MiB  </span><br><span class="line">    usage: 3.3 GiB used, 397 GiB &#x2F; 400 GiB avail  </span><br><span class="line">    pgs: 128 active+clean  </span><br><span class="line">  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p>通过 ceph health detail 查看集群问题  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph health  </span><br><span class="line">HEALTH_WARN 2 daemons have recently crashed  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph health detail  </span><br><span class="line">HEALTH_WARN 2 daemons have recently crashed  </span><br><span class="line">RECENT_CRASH 2 daemons have recently crashed  </span><br><span class="line">    mgr.ceph-node02 crashed on host ceph-node02 at 2020-10-03 01:53:00.058389Z  </span><br><span class="line">    mgr.ceph-node03 crashed on host ceph-node03 at 2020-10-03 03:33:30.776755Z  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph crash ls  </span><br><span class="line">ID ENTITY NEW  </span><br><span class="line">2020-10-03_01:53:00.058389Z_c26486ef-adab-4a1f-9b94-68953571e8d3 mgr.ceph-node02 *  </span><br><span class="line">2020-10-03_03:33:30.776755Z_88464c4c-0711-42fa-ae05-6196180cfe31 mgr.ceph-node03 *  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p>通过 systemctl restart ceph-mgr@ceph-node02无法重启（原因后续要找下），再次重建了下；</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph-deploy mgr create ceph-node02 ceph-node03</span><br></pre></td></tr></table></figure>



<p>再次查看集群状态如下，mgr已经恢复，但还提示两个进程crashed；  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph -s  </span><br><span class="line">  cluster:  </span><br><span class="line">    id: cc10b0cb-476f-420c-b1d6-e48c1dc929af  </span><br><span class="line">    health: HEALTH_WARN  </span><br><span class="line">            2 daemons have recently crashed  </span><br><span class="line">  </span><br><span class="line">  services:  </span><br><span class="line">    mon: 3 daemons, quorum ceph-node01,ceph-node02,ceph-node03 (age 15h)  </span><br><span class="line">    mgr: ceph-node01(active, since 15h), standbys: ceph-node02, ceph-node03  </span><br><span class="line">    osd: 3 osds: 3 up (since 30m), 3 in (since 30m)  </span><br><span class="line">  </span><br><span class="line">  data:  </span><br><span class="line">    pools: 1 pools, 128 pgs  </span><br><span class="line">    objects: 54 objects, 137 MiB  </span><br><span class="line">    usage: 3.3 GiB used, 397 GiB &#x2F; 400 GiB avail  </span><br><span class="line">    pgs: 128 active+clean  </span><br><span class="line">  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p>通过 ceph crash archive-all 或者 ID 的形式修复，再次查看集群状态如下：  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">[root@ceph-node01 ceph-deploy]# ceph crash archive-all  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#  </span><br><span class="line">[root@ceph-node01 ceph-deploy]# ceph -s  </span><br><span class="line">  cluster:  </span><br><span class="line">    id: cc10b0cb-476f-420c-b1d6-e48c1dc929af  </span><br><span class="line">    health: HEALTH_OK  </span><br><span class="line">  </span><br><span class="line">  services:  </span><br><span class="line">    mon: 3 daemons, quorum ceph-node01,ceph-node02,ceph-node03 (age 15h)  </span><br><span class="line">    mgr: ceph-node01(active, since 15h), standbys: ceph-node02, ceph-node03  </span><br><span class="line">    osd: 3 osds: 3 up (since 33m), 3 in (since 33m)  </span><br><span class="line">  </span><br><span class="line">  data:  </span><br><span class="line">    pools: 1 pools, 128 pgs  </span><br><span class="line">    objects: 54 objects, 137 MiB  </span><br><span class="line">    usage: 3.3 GiB used, 397 GiB &#x2F; 400 GiB avail  </span><br><span class="line">    pgs: 128 active+clean  </span><br><span class="line">  </span><br><span class="line">[root@ceph-node01 ceph-deploy]#</span><br></pre></td></tr></table></figure>



<p><strong>总结</strong></p>
<hr>
<p><strong>1. 安装前准备</strong></p>
<p>服务器规划、服务器间信任、主机名解析（hosts）、NTP同步、firewalld/iptables关闭、SELinux 关闭、配置yum源等；</p>
<p><strong>2. Ceph 集群部署</strong>  </p>
<p>mon 创建：ceph-deploy new –cluster-network 100.73.18.0/24 –public-network 100.73.18.0/24 <node-name> （创建第一个 mon ）</p>
<p>配置拷贝：ceph-deploy admin ceph-node01 ceph-node02 ceph-node03</p>
<p>mon 扩展：ceph-deploy mon add ceph-node02</p>
<p>mgr 创建：ceph-deploy mgr create ceph-node01</p>
<p>mgr 扩展：ceph-deploy mgr create ceph-node02 ceph-node03</p>
<p>osd 创建：ceph-deploy osd create ceph-node01 –data /dev/vdb</p>
<p><strong>3. 集群信息查看</strong></p>
<p>列出可用 osd：ceph-deploy osd list ceph-node01（列出某节点可用osd）</p>
<p>查看磁盘信息：lsblk；</p>
<p>擦除已有盘数据使其成为osd加入集群：ceph-deploy disk zap ceph-node01 /dev/vdb；</p>
<p>查看 OSD 信息：ceph osd tree、ceph osd stat、ceph osd ls、ceph osd dump等</p>
<p>查看 mon 选举：ceph quorum_status、ceph mon stat、ceph mon dump等</p>
<p><strong>4. 集群故障</strong></p>
<p>停止故障 OSD：ceph osd out {osd-num}</p>
<p>停止故障 OSD 进程：systemctl stop ceph-osd@{osd-num} </p>
<p>移出故障 OSD：ceph osd purge {id} –yes-i-really-mean-it</p>
<p>故障信息查看：ceph health、ceph health detail</p>
<p>查看crash进程：ceph crash ls</p>
<p>修复后忽略：ceph crash archive-all</p>
<p><strong>您的关注是我写作的动力</strong></p>
<hr>
<p><strong>基础小知识</strong></p>
<hr>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247484783&idx=1&sn=d4fdf3d489b7640442601476b8d4b1fd&chksm=e9fdd09bde8a598d6dd319a58073916f286def9f3bf7de6a9d6cc5a50f4cb9165436f9e2f31f&scene=21#wechat_redirect">hping 命令使用小结</a>  </p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247484796&idx=1&sn=42cc6c1a13ee575dc31997675da76c05&chksm=e9fdd088de8a599e96a403555c0a629767e748302815ae04fa8cef87a741343c776ddde544ae&scene=21#wechat_redirect">Linux 网卡 bonding 小知识</a>  </p>
<p><strong>专辑分享</strong></p>
<hr>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247483891&idx=1&sn=17dcd7cd0645df509c8e49059a2f00d7&chksm=e9fdd407de8a5d119d439b70dc2c381ec2eceddb63ed43767c2e1b7cffefe077e41955568cb5&scene=21#wechat_redirect">kubeadm使用外部etcd部署kubernetes v1.17.3 高可用集群</a>  </p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247484257&idx=1&sn=c666cf13ec5042a2c40dd0ccf89cc1eb&chksm=e9fdd695de8a5f8332bf51723043137032e65984dab81295616c1e13f55560088d8a6546fe78&scene=21#wechat_redirect">第一篇  使用 Prometheus 监控 Kubernetes 集群理论篇</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247485232&idx=1&sn=ff0e93b91432a68699e0e00a96602b78&chksm=e9fdd2c4de8a5bd22d4801cf35f78ffd9d7ab95b2a254bc5a4d181d9247c31c9b2f5485d4b74&scene=21#wechat_redirect">Ceph 基础篇 - 存储基础及架构介绍</a>  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>


<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/k8s/" rel="tag">k8s</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-develop/快速理解Cookie、Session、Token、JWT"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3Cookie%E3%80%81Session%E3%80%81Token%E3%80%81JWT/"
    >快速理解Cookie、Session、Token、JWT.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/%E5%BF%AB%E9%80%9F%E7%90%86%E8%A7%A3Cookie%E3%80%81Session%E3%80%81Token%E3%80%81JWT/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="快速理解Cookie、Session、Token、JWT"><a href="#快速理解Cookie、Session、Token、JWT" class="headerlink" title="快速理解Cookie、Session、Token、JWT"></a>快速理解Cookie、Session、Token、JWT</h1><h2 id="什么是认证（Authentication）"><a href="#什么是认证（Authentication）" class="headerlink" title="什么是认证（Authentication）"></a>什么是认证（Authentication）</h2><ul>
<li><p>通俗地讲就是<strong>验证当前用户的身份</strong>，证明 “你是你自己”（比如：你每天上下班打卡，都需要通过指纹打卡，当你的指纹和系统里录入的指纹相匹配时，就打卡成功）</p>
</li>
<li><p>互联网中的认证：</p>
</li>
<li><p>用户名密码登录</p>
</li>
<li><p>邮箱发送登录链接</p>
</li>
<li><p>手机号接收验证码</p>
</li>
<li><p>只要你能收到邮箱 / 验证码，就默认你是账号的主人</p>
</li>
</ul>
<h2 id="什么是授权（Authorization）"><a href="#什么是授权（Authorization）" class="headerlink" title="什么是授权（Authorization）"></a>什么是授权（Authorization）</h2><ul>
<li><p><strong>用户授予第三方应用访问该用户某些资源的权限</strong></p>
</li>
<li><p>你在安装手机应用的时候，APP 会询问是否允许授予权限（访问相册、地理位置等权限）</p>
</li>
<li><p>你在访问微信小程序时，当登录时，小程序会询问是否允许授予权限（获取昵称、头像、地区、性别等个人信息）</p>
</li>
<li><p>实现授权的方式有：cookie、session、token、OAuth</p>
</li>
</ul>
<h2 id="什么是凭证（Credentials）"><a href="#什么是凭证（Credentials）" class="headerlink" title="什么是凭证（Credentials）"></a>什么是凭证（Credentials）</h2><ul>
<li><p><strong>实现认证和授权的前提</strong>是需要一种<strong>媒介（证书）</strong> 来标记访问者的身份</p>
</li>
<li><p>在战国时期，商鞅变法，发明了照身帖。照身帖由官府发放，是一块打磨光滑细密的竹板，上面刻有持有人的头像和籍贯信息。国人必须持有，如若没有就被认为是黑户，或者间谍之类的。</p>
</li>
<li><p>在现实生活中，每个人都会有一张专属的居民身份证，是用于证明持有人身份的一种法定证件。通过身份证，我们可以办理手机卡 / 银行卡 / 个人贷款 / 交通出行等等，这就是<strong>认证的凭证。</strong></p>
</li>
<li><p>在互联网应用中，一般网站（如掘金）会有两种模式，游客模式和登录模式。游客模式下，可以正常浏览网站上面的文章，一旦想要点赞 / 收藏 / 分享文章，就需要登录或者注册账号。当用户登录成功后，服务器会给该用户使用的浏览器颁发一个令牌（token），这个令牌用来表明你的身份，每次浏览器发送请求时会带上这个令牌，就可以使用游客模式下无法使用的功能。</p>
</li>
</ul>
<h2 id="什么是-Cookie"><a href="#什么是-Cookie" class="headerlink" title="什么是 Cookie"></a>什么是 Cookie</h2><ul>
<li><p><strong>HTTP 是无状态的协议（对于事务处理没有记忆能力，每次客户端和服务端会话完成时，服务端不会保存任何会话信息</strong>）：每个请求都是完全独立的，服务端无法确认当前访问者的身份信息，无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。所以服务器与浏览器为了进行会话跟踪（知道是谁在访问我），就必须主动的去维护一个状态，这个状态用于告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过 cookie 或者 session 去实现。</p>
</li>
<li><p><strong>cookie 存储在客户端：</strong> cookie 是服务器发送到用户浏览器并保存在本地的一小块数据，它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。</p>
</li>
<li><p><strong>cookie 是不可跨域的：</strong> 每个 cookie 都会绑定单一的域名，无法在别的域名下获取使用，<strong>一级域名和二级域名之间是允许共享使用的</strong>（<strong>靠的是 domain）</strong>。</p>
</li>
</ul>
<p><strong>cookie 重要的属性</strong></p>
<table width="751"><thead style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><th style="box-sizing: border-box;padding: 0.5rem 1rem;text-align: left;border-top-width: 1px;border-color: rgb(233, 235, 236);">属性</th><th style="box-sizing: border-box;padding: 0.5rem 1rem;text-align: left;border-top-width: 1px;border-color: rgb(233, 235, 236);">说明</th></tr></thead><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><td style="box-sizing: border-box;padding: 0.5rem 1rem;border-color: rgb(233, 235, 236);"><strong style="box-sizing: border-box;color: rgb(0, 0, 0);">name=value</strong></td><td style="box-sizing: border-box;padding: 0.5rem 1rem;border-color: rgb(233, 235, 236);">键值对，设置 Cookie 的名称及相对应的值，都必须是<strong style="box-sizing: border-box;color: rgb(0, 0, 0);">字符串类型</strong></td></tr></tbody></table>

<ul>
<li><p>如果值为 Unicode 字符，需要为字符编码。</p>
</li>
<li><p>如果值为二进制数据，则需要使用 BASE64 编码。<br>| | <strong>domain</strong> | 指定 cookie 所属域名，默认是当前域名 | | <strong>path</strong> | <strong>指定 cookie 在哪个路径（路由）下生效，默认是 ‘/‘**。<br>如果设置为 <code>/abc</code>，则只有 <code>/abc</code> 下的路由可以访问到该 cookie，如：<code>/abc/read</code>。| | **maxAge</strong> | cookie 失效的时间，单位秒。如果为整数，则该 cookie 在 maxAge 秒后失效。如果为负数，该 cookie 为临时 cookie ，关闭浏览器即失效，浏览器也不会以任何形式保存该 cookie 。如果为 0，表示删除该 cookie 。默认为 -1。</p>
</li>
<li><p><strong>比 expires 好用</strong>。<br>| | <strong>expires</strong> | 过期时间，在设置的某个时间点后该 cookie 就会失效。<br>一般浏览器的 cookie 都是默认储存的，当关闭浏览器结束这个会话的时候，这个 cookie 也就会被删除 | | <strong>secure</strong> | 该 cookie 是否仅被使用安全协议传输。安全协议有 HTTPS，SSL 等，在网络上传输数据之前先将数据加密。默认为 false。<br>当 secure 值为 true 时，cookie 在 HTTP 中是无效，在 HTTPS 中才有效。| | <strong>httpOnly</strong> | <strong>如果给某个 cookie 设置了 httpOnly 属性，则无法通过 JS 脚本 读取到该 cookie 的信息，但还是能通过 Application 中手动修改 cookie，所以只是在一定程度上可以防止 XSS 攻击，不是绝对的安全</strong> |</p>
</li>
</ul>
<h2 id="什么是-Session"><a href="#什么是-Session" class="headerlink" title="什么是 Session"></a>什么是 Session</h2><ul>
<li><p><strong>session 是另一种记录服务器和客户端会话状态的机制</strong></p>
</li>
<li><p><strong>session 是基于 cookie 实现的，session 存储在服务器端，sessionId 会被存储到客户端的 cookie 中</strong></p>
</li>
</ul>
<p><img src=""></p>
<ul>
<li><p><strong>session 认证流程：</strong></p>
</li>
<li><p>用户第一次请求服务器的时候，服务器根据用户提交的相关信息，创建对应的 Session</p>
</li>
<li><p>请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器</p>
</li>
<li><p>浏览器接收到服务器返回的 SessionID 信息后，会将此信息存入到 Cookie 中，同时 Cookie 记录此 SessionID 属于哪个域名</p>
</li>
<li><p>当用户第二次访问服务器的时候，请求会自动判断此域名下是否存在 Cookie 信息，如果存在自动将 Cookie 信息也发送给服务端，服务端会从 Cookie 中获取 SessionID，再根据 SessionID 查找对应的 Session 信息，如果没有找到说明用户没有登录或者登录失效，如果找到 Session 证明用户已经登录可执行后面操作。</p>
</li>
</ul>
<p>根据以上流程可知，<strong>SessionID 是连接 Cookie 和 Session 的一道桥梁</strong>，大部分系统也是根据此原理来验证用户登录状态。</p>
<h2 id="Cookie-和-Session-的区别"><a href="#Cookie-和-Session-的区别" class="headerlink" title="Cookie 和 Session 的区别"></a>Cookie 和 Session 的区别</h2><ul>
<li><p><strong>安全性：</strong> Session 比 Cookie 安全，Session 是存储在服务器端的，Cookie 是存储在客户端的。</p>
</li>
<li><p><strong>存取值的类型不同</strong>：Cookie 只支持存字符串数据，想要设置其他类型的数据，需要将其转换成字符串，Session 可以存任意数据类型。</p>
</li>
<li><p><strong>有效期不同：</strong> Cookie 可设置为长时间保持，比如我们经常使用的默认登录功能，Session 一般失效时间较短，客户端关闭（默认情况下）或者 Session 超时都会失效。</p>
</li>
<li><p><strong>存储大小不同：</strong> 单个 Cookie 保存的数据不能超过 4K，Session 可存储数据远高于 Cookie，但是当访问量过多，会占用过多的服务器资源。</p>
</li>
</ul>
<h2 id="什么是-Token（令牌）"><a href="#什么是-Token（令牌）" class="headerlink" title="什么是 Token（令牌）"></a>什么是 Token（令牌）</h2><h3 id="Acesss-Token"><a href="#Acesss-Token" class="headerlink" title="Acesss Token"></a>Acesss Token</h3><ul>
<li><p><strong>访问资源接口（API）时所需要的资源凭证</strong></p>
</li>
<li><p><strong>简单 token 的组成：</strong> uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign（签名，token 的前几位以哈希算法压缩成的一定长度的十六进制字符串）</p>
</li>
<li><p><strong>特点：</strong></p>
</li>
<li><p><strong>服务端无状态化、可扩展性好</strong></p>
</li>
<li><p><strong>支持移动端设备</strong></p>
</li>
<li><p>安全</p>
</li>
<li><p>支持跨程序调用</p>
</li>
<li><p><strong>token 的身份验证流程：</strong></p>
</li>
</ul>
<p><img src=""></p>
<ol>
<li><p>客户端使用用户名跟密码请求登录</p>
</li>
<li><p>服务端收到请求，去验证用户名与密码</p>
</li>
<li><p>验证成功后，服务端会签发一个 token 并把这个 token 发送给客户端</p>
</li>
<li><p>客户端收到 token 以后，会把它存储起来，比如放在 cookie 里或者 localStorage 里</p>
</li>
<li><p>客户端每次向服务端请求资源的时候需要带着服务端签发的 token</p>
</li>
<li><p>服务端收到请求，然后去验证客户端请求里面带着的 token ，如果验证成功，就向客户端返回请求的数据</p>
</li>
</ol>
<ul>
<li><p><strong>每一次请求都需要携带 token，需要把 token 放到 HTTP 的 Header 里</strong></p>
</li>
<li><p><strong>基于 token 的用户认证是一种服务端无状态的认证方式，服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间，从而减轻服务器的压力，减少频繁的查询数据库</strong></p>
</li>
<li><p><strong>token 完全由应用管理，所以它可以避开同源策略</strong></p>
</li>
</ul>
<h3 id="Refresh-Token"><a href="#Refresh-Token" class="headerlink" title="Refresh Token"></a>Refresh Token</h3><ul>
<li><p>另外一种 token——refresh token</p>
</li>
<li><p>refresh token 是专用于刷新 access token 的 token。如果没有 refresh token，也可以刷新 access token，但每次刷新都要用户输入登录用户名与密码，会很麻烦。有了 refresh token，可以减少这个麻烦，客户端直接用 refresh token 去更新 access token，无需用户进行额外的操作。</p>
</li>
</ul>
<p><img src=""></p>
<ul>
<li><p>Access Token 的有效期比较短，当 Acesss Token 由于过期而失效时，使用 Refresh Token 就可以获取到新的 Token，如果 Refresh Token 也失效了，用户就只能重新登录了。</p>
</li>
<li><p>Refresh Token 及过期时间是存储在服务器的数据库中，只有在申请新的 Acesss Token 时才会验证，不会对业务接口响应时间造成影响，也不需要向 Session 一样一直保持在内存中以应对大量的请求。</p>
</li>
</ul>
<h2 id="Token-和-Session-的区别"><a href="#Token-和-Session-的区别" class="headerlink" title="Token 和 Session 的区别"></a>Token 和 Session 的区别</h2><ul>
<li><p>Session 是一种<strong>记录服务器和客户端会话状态的机制，使服务端有状态化，可以记录会话信息</strong>。而 Token 是<strong>令牌</strong>，<strong>访问资源接口（API）时所需要的资源凭证</strong>。Token <strong>使服务端无状态化，不会存储会话信息。</strong></p>
</li>
<li><p>Session 和 Token 并不矛盾，作为身份认证 Token 安全性比 Session 好，因为每一个请求都有签名还能防止监听以及重放攻击，而 Session 就必须依赖链路层来保障通讯安全了。<strong>如果你需要实现有状态的会话，仍然可以增加 Session 来在服务器端保存一些状态。</strong></p>
</li>
<li><p>所谓 Session 认证只是简单的把 User 信息存储到 Session 里，因为 SessionID 的不可预测性，暂且认为是安全的。而 Token ，如果指的是 OAuth Token 或类似的机制的话，提供的是 认证 和 授权 ，认证是针对用户，授权是针对 App 。其目的是让某 App 有权利访问某用户的信息。这里的 Token 是唯一的。不可以转移到其它 App 上，也不可以转到其它用户上。Session 只提供一种简单的认证，即只要有此 SessionID ，即认为有此 User 的全部权利。是需要严格保密的，这个数据应该只保存在站方，不应该共享给其它网站或者第三方 App。所以简单来说：<strong>如果你的用户数据可能需要和第三方共享，或者允许第三方调用 API 接口，用 Token 。如果永远只是自己的网站，自己的 App，用什么就无所谓了。</strong></p>
</li>
</ul>
<h2 id="什么是-JWT"><a href="#什么是-JWT" class="headerlink" title="什么是 JWT"></a>什么是 JWT</h2><ul>
<li><p>JSON Web Token（简称 JWT）是目前最流行的<strong>跨域认证</strong>解决方案。</p>
</li>
<li><p>是一种<strong>认证授权机制</strong>。</p>
</li>
<li><p>JWT 是为了在网络应用环境间<strong>传递声明</strong>而执行的一种基于 JSON 的开放标准（RFC 7519）。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息，以便于从资源服务器获取资源。比如用在用户登录上。</p>
</li>
<li><p>可以使用 HMAC 算法或者是 RSA 的公 / 私秘钥对 JWT 进行签名。因为数字签名的存在，这些传递的信息是可信的。</p>
</li>
<li><p><strong>阮一峰老师的 JSON Web Token 入门教程 讲的非常通俗易懂，这里就不再班门弄斧了</strong></p>
</li>
</ul>
<h3 id="生成-JWT"><a href="#生成-JWT" class="headerlink" title="生成 JWT"></a>生成 JWT</h3><p>jwt.io/<br><a target="_blank" rel="noopener" href="http://www.jsonwebtoken.io/">www.jsonwebtoken.io/</a></p>
<h3 id="JWT-的原理"><a href="#JWT-的原理" class="headerlink" title="JWT 的原理"></a>JWT 的原理</h3><p><img src=""></p>
<ul>
<li><p><strong>JWT 认证流程：</strong></p>
</li>
<li><p>用户输入用户名 / 密码登录，服务端认证成功后，会返回给客户端一个 JWT</p>
</li>
<li><p>客户端将 token 保存到本地（通常使用 localstorage，也可以使用 cookie）</p>
</li>
<li><p>当用户希望访问一个受保护的路由或者资源的时候，需要请求头的 Authorization 字段中使用 Bearer 模式添加 JWT，其内容看起来是下面这样</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Authorization: Bearer &lt;token&gt;  </span><br><span class="line">  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li><p>服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息，如果合法，则允许用户的行为</p>
</li>
<li><p>因为 JWT 是自包含的（内部包含了一些会话信息），因此减少了需要查询数据库的需要</p>
</li>
<li><p>因为 JWT 并不使用 Cookie 的，所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题（CORS）</p>
</li>
<li><p>因为用户的状态不再存储在服务端的内存中，所以这是一种无状态的认证机制</p>
</li>
</ul>
<h3 id="JWT-的使用方式"><a href="#JWT-的使用方式" class="headerlink" title="JWT 的使用方式"></a>JWT 的使用方式</h3><ul>
<li>客户端收到服务器返回的 JWT，可以储存在 Cookie 里面，也可以储存在 localStorage。</li>
</ul>
<h4 id="方式一"><a href="#方式一" class="headerlink" title="方式一"></a>方式一</h4><ul>
<li><p>当用户希望访问一个受保护的路由或者资源的时候，可以把它放在 Cookie 里面自动发送，但是这样不能跨域，所以更好的做法是放在 HTTP 请求头信息的 Authorization 字段里，使用 Bearer 模式添加 JWT。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">GET &#x2F;calendar&#x2F;v1&#x2F;events  </span><br><span class="line">Host: api.example.com  </span><br><span class="line">Authorization: Bearer &lt;token&gt;  </span><br><span class="line">复制代码  </span><br><span class="line">  </span><br><span class="line"></span><br></pre></td></tr></table></figure>
</li>
<li><p>用户的状态不会存储在服务端的内存中，这是一种 <strong>无状态的认证机制</strong></p>
</li>
<li><p>服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息，如果合法，则允许用户的行为。</p>
</li>
<li><p>由于 JWT 是自包含的，因此减少了需要查询数据库的需要</p>
</li>
<li><p>JWT 的这些特性使得我们可以完全依赖其无状态的特性提供数据 API 服务，甚至是创建一个下载流服务。</p>
</li>
<li><p>因为 JWT 并不使用 Cookie ，所以你可以使用任何域名提供你的 API 服务而<strong>不需要担心跨域资源共享问题</strong>（CORS）</p>
</li>
</ul>
<h4 id="方式二"><a href="#方式二" class="headerlink" title="方式二"></a>方式二</h4><ul>
<li>跨域的时候，可以把 JWT 放在 POST 请求的数据体里。</li>
</ul>
<h4 id="方式三"><a href="#方式三" class="headerlink" title="方式三"></a>方式三</h4><ul>
<li>通过 URL 传输</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">http:&#x2F;&#x2F;www.example.com&#x2F;user?token&#x3D;xxx  </span><br><span class="line">  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="项目中使用-JWT"><a href="#项目中使用-JWT" class="headerlink" title="项目中使用 JWT"></a>项目中使用 JWT</h3><p><strong>项目地址</strong></p>
<p>Token 和 JWT 的区别</p>
<hr>
<p><strong>相同：</strong></p>
<ul>
<li><p>都是访问资源的令牌</p>
</li>
<li><p>都可以记录用户的信息</p>
</li>
<li><p>都是使服务端无状态化</p>
</li>
<li><p>都是只有验证成功后，客户端才能访问服务端上受保护的资源</p>
</li>
</ul>
<p><strong>区别：</strong></p>
<ul>
<li><p>Token：服务端验证客户端发送过来的 Token 时，还需要查询数据库获取用户信息，然后验证 Token 是否有效。</p>
</li>
<li><p>JWT：将 Token 和 Payload 加密后存储于客户端，服务端只需要使用密钥解密进行校验（校验也是 JWT 自己实现的）即可，不需要查询或者减少查询数据库，因为 JWT 自包含了用户信息和加密的数据。</p>
</li>
</ul>
<p>常见的前后端鉴权方式</p>
<hr>
<ol>
<li><p>Session-Cookie</p>
</li>
<li><p>Token 验证（包括 JWT，SSO）</p>
</li>
<li><p>OAuth2.0（开放授权）</p>
</li>
</ol>
<h2 id="常见的加密算法"><a href="#常见的加密算法" class="headerlink" title="常见的加密算法"></a>常见的加密算法</h2><p><img src=""></p>
<ul>
<li><p>哈希算法 (Hash Algorithm) 又称散列算法、散列函数、哈希函数，是一种从任何一种数据中创建小的数字 “指纹” 的方法。哈希算法将数据重新打乱混合，重新创建一个哈希值。</p>
</li>
<li><p>哈希算法主要用来保障数据真实性 (即完整性)，即发信人将原始消息和哈希值一起发送，收信人通过相同的哈希函数来校验原始数据是否真实。</p>
</li>
<li><p>哈希算法通常有以下几个特点：</p>
</li>
<li><p>2 的 128 次方为 340282366920938463463374607431768211456，也就是 10 的 39 次方级别</p>
</li>
<li><p>2 的 160 次方为 1.4615016373309029182036848327163e+48，也就是 10 的 48 次方级别</p>
</li>
<li><p>2 的 256 次方为 1.1579208923731619542357098500869 × 10 的 77 次方，也就是 10 的 77 次方</p>
</li>
<li><p>正像快速：原始数据可以快速计算出哈希值</p>
</li>
<li><p>逆向困难：通过哈希值基本不可能推导出原始数据</p>
</li>
<li><p>输入敏感：原始数据只要有一点变动，得到的哈希值差别很大</p>
</li>
<li><p>冲突避免：很难找到不同的原始数据得到相同的哈希值，宇宙中原子数大约在 10 的 60 次方到 80 次方之间，所以 2 的 256 次方有足够的空间容纳所有的可能，算法好的情况下冲突碰撞的概率很低：</p>
</li>
</ul>
<p><strong>注意：</strong></p>
<ol>
<li><p>以上不能保证数据被恶意篡改，原始数据和哈希值都可能被恶意篡改，要保证不被篡改，可以使用 RSA 公钥私钥方案，再配合哈希值。</p>
</li>
<li><p>哈希算法主要用来防止计算机传输过程中的错误，早期计算机通过前 7 位数据第 8 位奇偶校验码来保障（12.5% 的浪费效率低），对于一段数据或文件，通过哈希算法生成 128bit 或者 256bit 的哈希值，如果校验有问题就要求重传。</p>
</li>
</ol>
<h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="使用-cookie-时需要考虑的问题"><a href="#使用-cookie-时需要考虑的问题" class="headerlink" title="使用 cookie 时需要考虑的问题"></a>使用 cookie 时需要考虑的问题</h3><ul>
<li><p>因为存储在客户端，容易被客户端篡改，使用前需要验证合法性</p>
</li>
<li><p>不要存储敏感数据，比如用户密码，账户余额</p>
</li>
<li><p>使用 httpOnly 在一定程度上提高安全性</p>
</li>
<li><p>尽量减少 cookie 的体积，能存储的数据量不能超过 4kb</p>
</li>
<li><p>设置正确的 domain 和 path，减少数据传输</p>
</li>
<li><p><strong>cookie 无法跨域</strong></p>
</li>
<li><p>一个浏览器针对一个网站最多存 20 个 Cookie，浏览器一般只允许存放 300 个 Cookie</p>
</li>
<li><p><strong>移动端对 cookie 的支持不是很好，而 session 需要基于 cookie 实现，所以移动端常用的是 token</strong></p>
</li>
</ul>
<h3 id="使用-session-时需要考虑的问题"><a href="#使用-session-时需要考虑的问题" class="headerlink" title="使用 session 时需要考虑的问题"></a>使用 session 时需要考虑的问题</h3><ul>
<li><p>将 session 存储在服务器里面，当用户同时在线量比较多时，这些 session 会占据较多的内存，需要在服务端定期的去清理过期的 session</p>
</li>
<li><p>当网站采用<strong>集群部署</strong>的时候，会遇到多台 web 服务器之间如何做 session 共享的问题。因为 session 是由单个服务器创建的，但是处理用户请求的服务器不一定是那个创建 session 的服务器，那么该服务器就无法拿到之前已经放入到 session 中的登录凭证之类的信息了。</p>
</li>
<li><p>当多个应用要共享 session 时，除了以上问题，还会遇到跨域问题，因为不同的应用可能部署的主机不一样，需要在各个应用做好 cookie 跨域的处理。</p>
</li>
<li><p><strong>sessionId 是存储在 cookie 中的，假如浏览器禁止 cookie 或不支持 cookie 怎么办？</strong> 一般会把 sessionId 跟在 url 参数后面即重写 url，所以 session 不一定非得需要靠 cookie 实现</p>
</li>
<li><p><strong>移动端对 cookie 的支持不是很好，而 session 需要基于 cookie 实现，所以移动端常用的是 token</strong></p>
</li>
</ul>
<h3 id="使用-token-时需要考虑的问题"><a href="#使用-token-时需要考虑的问题" class="headerlink" title="使用 token 时需要考虑的问题"></a>使用 token 时需要考虑的问题</h3><ul>
<li><p>如果你认为用数据库来存储 token 会导致查询时间太长，可以选择放在内存当中。比如 redis 很适合你对 token 查询的需求。</p>
</li>
<li><p><strong>token 完全由应用管理，所以它可以避开同源策略</strong></p>
</li>
<li><p><strong>token 可以避免 CSRF 攻击 (因为不需要 cookie 了)</strong></p>
</li>
<li><p><strong>移动端对 cookie 的支持不是很好，而 session 需要基于 cookie 实现，所以移动端常用的是 token</strong></p>
</li>
</ul>
<h3 id="使用-JWT-时需要考虑的问题"><a href="#使用-JWT-时需要考虑的问题" class="headerlink" title="使用 JWT 时需要考虑的问题"></a>使用 JWT 时需要考虑的问题</h3><ul>
<li><p>因为 JWT 并不依赖 Cookie 的，所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题（CORS）</p>
</li>
<li><p>JWT 默认是不加密，但也是可以加密的。生成原始 Token 以后，可以用密钥再加密一次。</p>
</li>
<li><p>JWT 不加密的情况下，不能将秘密数据写入 JWT。</p>
</li>
<li><p>JWT 不仅可以用于认证，也可以用于交换信息。有效使用 JWT，可以降低服务器查询数据库的次数。</p>
</li>
<li><p>JWT 最大的优势是服务器不再需要存储 Session，使得服务器认证鉴权业务可以方便扩展。但这也是 JWT 最大的缺点：由于服务器不需要存储 Session 状态，因此使用过程中无法废弃某个 Token 或者更改 Token 的权限。也就是说一旦 JWT 签发了，到期之前就会始终有效，除非服务器部署额外的逻辑。</p>
</li>
<li><p>JWT 本身包含了认证信息，一旦泄露，任何人都可以获得该令牌的所有权限。为了减少盗用，JWT 的有效期应该设置得比较短。对于一些比较重要的权限，使用时应该再次对用户进行认证。</p>
</li>
<li><p>JWT 适合一次性的命令认证，颁发一个有效期极短的 JWT，即使暴露了危险也很小，由于每次操作都会生成新的 JWT，因此也没必要保存 JWT，真正实现无状态。</p>
</li>
<li><p>为了减少盗用，JWT 不应该使用 HTTP 协议明码传输，要使用 HTTPS 协议传输。</p>
</li>
</ul>
<h3 id="使用加密算法时需要考虑的问题"><a href="#使用加密算法时需要考虑的问题" class="headerlink" title="使用加密算法时需要考虑的问题"></a>使用加密算法时需要考虑的问题</h3><ul>
<li><p>绝不要以<strong>明文存储</strong>密码</p>
</li>
<li><p><strong>永远使用 哈希算法 来处理密码，绝不要使用 Base64 或其他编码方式来存储密码，这和以明文存储密码是一样的，使用哈希，而不要使用编码</strong>。编码以及加密，都是双向的过程，而密码是保密的，应该只被它的所有者知道， 这个过程必须是单向的。哈希正是用于做这个的，从来没有解哈希这种说法， 但是编码就存在解码，加密就存在解密。</p>
</li>
<li><p>绝不要使用弱哈希或已被破解的哈希算法，像 MD5 或 SHA1 ，只使用强密码哈希算法。</p>
</li>
<li><p>绝不要以明文形式显示或发送密码，即使是对密码的所有者也应该这样。如果你需要 “忘记密码” 的功能，可以随机生成一个新的 <strong>一次性的</strong>（这点很重要）密码，然后把这个密码发送给用户。</p>
</li>
</ul>
<h3 id="分布式架构下-session-共享方案"><a href="#分布式架构下-session-共享方案" class="headerlink" title="分布式架构下 session 共享方案"></a>分布式架构下 session 共享方案</h3><h4 id="1-session-复制"><a href="#1-session-复制" class="headerlink" title="1. session 复制"></a>1. session 复制</h4><ul>
<li>任何一个服务器上的 session 发生改变（增删改），该节点会把这个 session 的所有内容序列化，然后广播给所有其它节点，不管其他服务器需不需要 session ，以此来保证 session 同步</li>
</ul>
<p><strong>优点：</strong> 可容错，各个服务器间 session 能够实时响应。<br><strong>缺点：</strong> 会对网络负荷造成一定压力，如果 session 量大的话可能会造成网络堵塞，拖慢服务器性能。</p>
<h4 id="2-粘性-session-IP-绑定策略"><a href="#2-粘性-session-IP-绑定策略" class="headerlink" title="2. 粘性 session /IP 绑定策略"></a>2. 粘性 session /IP 绑定策略</h4><ul>
<li><strong>采用 Ngnix 中的 ip_hash 机制，将某个 ip 的所有请求都定向到同一台服务器上，即将用户与服务器绑定。</strong> 用户第一次请求时，负载均衡器将用户的请求转发到了 A 服务器上，如果负载均衡器设置了粘性 session 的话，那么用户以后的每次请求都会转发到 A 服务器上，相当于把用户和 A 服务器粘到了一块，这就是粘性 session 机制。</li>
</ul>
<p><strong>优点：</strong> 简单，不需要对 session 做任何处理。<br><strong>缺点：</strong> 缺乏容错性，如果当前访问的服务器发生故障，用户被转移到第二个服务器上时，他的 session 信息都将失效。<br><strong>适用场景：</strong> 发生故障对客户产生的影响较小；服务器发生故障是低概率事件 。<br><strong>实现方式：</strong> 以 Nginx 为例，在 upstream 模块配置 ip_hash 属性即可实现粘性 session。</p>
<h4 id="3-session-共享（常用）"><a href="#3-session-共享（常用）" class="headerlink" title="3. session 共享（常用）"></a>3. session 共享（常用）</h4><ul>
<li><p>使用分布式缓存方案比如 Memcached 、Redis 来缓存 session，但是要求 Memcached 或 Redis 必须是集群</p>
</li>
<li><p>把 session 放到 Redis 中存储，虽然架构上变得复杂，并且需要多访问一次 Redis ，但是这种方案带来的好处也是很大的：</p>
</li>
<li><p>实现了 session 共享；</p>
</li>
<li><p>可以水平扩展（增加 Redis 服务器）；</p>
</li>
<li><p>服务器重启 session 不丢失（不过也要注意 session 在 Redis 中的刷新 / 失效机制）；</p>
</li>
<li><p>不仅可以跨服务器 session 共享，甚至可以跨平台（例如网页端和 APP 端）</p>
</li>
</ul>
<p><img src=""></p>
<h4 id="4-session-持久化"><a href="#4-session-持久化" class="headerlink" title="4. session 持久化"></a>4. session 持久化</h4><ul>
<li>将 session 存储到数据库中，保证 session 的持久化</li>
</ul>
<p><strong>优点：</strong> 服务器出现问题，session 不会丢失<br><strong>缺点：</strong> 如果网站的访问量很大，把 session 存储到数据库中，会对数据库造成很大压力，还需要增加额外的开销维护数据库。</p>
<h3 id="只要关闭浏览器-，session-真的就消失了？"><a href="#只要关闭浏览器-，session-真的就消失了？" class="headerlink" title="只要关闭浏览器 ，session 真的就消失了？"></a><strong>只要关闭浏览器 ，session 真的就消失了？</strong></h3><p>不对。对 session 来说，除非程序通知服务器删除一个 session，否则服务器会一直保留，程序一般都是在用户做 log off 的时候发个指令去删除 session。<br>然而浏览器从来不会主动在关闭之前通知服务器它将要关闭，因此服务器根本不会有机会知道浏览器已经关闭，之所以会有这种错觉，是大部分 session 机制都使用会话 cookie 来保存 session id，而关闭浏览器后这个 session id 就消失了，再次连接服务器时也就无法找到原来的 session。如果服务器设置的 cookie 被保存在硬盘上，或者使用某种手段改写浏览器发出的 HTTP 请求头，把原来的 session id 发送给服务器，则再次打开浏览器仍然能够打开原来的 session。<br>恰恰是<strong>由于关闭浏览器不会导致 session 被删除，迫使服务器为 session 设置了一个失效时间，当距离客户端上一次使用 session 的时间超过这个失效时间时，服务器就认为客户端已经停止了活动，才会把 session 删除以节省存储空间。</strong></p>
<h2 id="项目地址"><a href="#项目地址" class="headerlink" title="项目地址"></a>项目地址</h2><p><strong>在项目中使用 JWT</strong>:<a target="_blank" rel="noopener" href="https://github.com/yjdjiayou/jwt-demo">https://github.com/yjdjiayou/jwt-demo</a></p>
<h2 id="后语"><a href="#后语" class="headerlink" title="后语"></a>后语</h2><ul>
<li><p>本文只是基于自己的理解讲了理论知识，因为对后端 / 算法知识不是很熟，如有谬误，还请告知，万分感谢</p>
</li>
<li><p>如果本文对你有所帮助，还请点个赞~~</p>
</li>
</ul>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-other/一文搞懂蓝绿发布、灰度发布和滚动发布"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/other/%E4%B8%80%E6%96%87%E6%90%9E%E6%87%82%E8%93%9D%E7%BB%BF%E5%8F%91%E5%B8%83%E3%80%81%E7%81%B0%E5%BA%A6%E5%8F%91%E5%B8%83%E5%92%8C%E6%BB%9A%E5%8A%A8%E5%8F%91%E5%B8%83/"
    >一文搞懂蓝绿发布、灰度发布和滚动发布.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/other/%E4%B8%80%E6%96%87%E6%90%9E%E6%87%82%E8%93%9D%E7%BB%BF%E5%8F%91%E5%B8%83%E3%80%81%E7%81%B0%E5%BA%A6%E5%8F%91%E5%B8%83%E5%92%8C%E6%BB%9A%E5%8A%A8%E5%8F%91%E5%B8%83/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/other/">other</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="一文搞懂蓝绿发布、灰度发布和滚动发布"><a href="#一文搞懂蓝绿发布、灰度发布和滚动发布" class="headerlink" title="一文搞懂蓝绿发布、灰度发布和滚动发布"></a>一文搞懂蓝绿发布、灰度发布和滚动发布</h1><h1 id="一、-蓝绿发布"><a href="#一、-蓝绿发布" class="headerlink" title="一、 蓝绿发布"></a>一、 蓝绿发布</h1><p>项目逻辑上分为AB组，在项目系统时，首先把A组从负载均衡中摘除，进行新版本的部署。B组仍然继续提供服务。  </p>
<p><img src=""></p>
<p>当A组升级完毕，负载均衡重新接入A组，再把B组从负载列表中摘除，进行新版本的部署。A组重新提供服务。    </p>
<p><img src=""></p>
<p>最后，B组也升级完成，负载均衡重新接入B组，此时，AB组版本都已经升级完成，并且都对外提供服务。</p>
<h2 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h2><ul>
<li><p>如果出问题，影响范围较大；</p>
</li>
<li><p>发布策略简单；</p>
</li>
<li><p>用户无感知，平滑过渡；</p>
</li>
<li><p>升级/回滚速度快。</p>
</li>
</ul>
<h2 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h2><ul>
<li><p>需要准备正常业务使用资源的两倍以上服务器，防止升级期间单组无法承载业务突发；</p>
</li>
<li><p>短时间内浪费一定资源成本；</p>
</li>
<li><p>基础设施无改动，增大升级稳定性。</p>
</li>
</ul>
<p>蓝绿发布在早期物理服务器时代，还是比较昂贵的，由于云计算普及，成本也大大降低。</p>
<h1 id="二、-灰度发布"><a href="#二、-灰度发布" class="headerlink" title="二、 灰度发布"></a>二、 灰度发布</h1><p>灰度发布只升级部分服务，即让一部分用户继续用老版本，一部分用户开始用新版本，如果用户对新版本没什么意见，那么逐步扩大范围，把所有用户都迁移到新版本上面来。  </p>
<p><img src=""></p>
<h2 id="特点-1"><a href="#特点-1" class="headerlink" title="特点"></a>特点</h2><ul>
<li><p>保证整体系统稳定性，在初始灰度的时候就可以发现、调整问题，影响范围可控；</p>
</li>
<li><p>新功能逐步评估性能，稳定性和健康状况，如果出问题影响范围很小，相对用户体验也少；</p>
</li>
<li><p>用户无感知，平滑过渡。</p>
</li>
</ul>
<h2 id="缺点-1"><a href="#缺点-1" class="headerlink" title="缺点"></a>缺点</h2><ul>
<li>自动化要求高</li>
</ul>
<h2 id="部署过程"><a href="#部署过程" class="headerlink" title="部署过程"></a>部署过程</h2><ul>
<li><p>从LB摘掉灰度服务器，升级成功后再加入LB；</p>
</li>
<li><p>少量用户流量到新版本；</p>
</li>
<li><p>如果灰度服务器测试成功，升级剩余服务器。</p>
</li>
</ul>
<p>灰度发布是通过切换线上并存版本之间的路由权重，逐步从一个版本切换为另一个版本的过程。</p>
<h1 id="三、-滚动发布"><a href="#三、-滚动发布" class="headerlink" title="三、 滚动发布"></a>三、 滚动发布</h1><p>滚动发布是指每次只升级一个或多个服务，升级完成后加入生产环境，不断执行这个过程，直到集群中的全部旧版本升级新版本。 </p>
<p><img src=""></p>
<ul>
<li><p>红色：正在更新的实例</p>
</li>
<li><p>蓝色：更新完成并加入集群的实例</p>
</li>
<li><p>绿色：正在运行的实例</p>
</li>
</ul>
<h2 id="特点-2"><a href="#特点-2" class="headerlink" title="特点"></a>特点</h2><ul>
<li><p>用户无感知，平滑过渡；</p>
</li>
<li><p>节约资源。</p>
</li>
</ul>
<h2 id="缺点-2"><a href="#缺点-2" class="headerlink" title="缺点"></a>缺点</h2><ul>
<li><p>部署时间慢，取决于每阶段更新时间；</p>
</li>
<li><p>发布策略较复杂；</p>
</li>
<li><p>无法确定OK的环境，不易回滚。</p>
</li>
</ul>
<h2 id="部署过程-1"><a href="#部署过程-1" class="headerlink" title="部署过程"></a>部署过程</h2><ul>
<li><p>先升级1个副本，主要做部署验证；</p>
</li>
<li><p>每次升级副本，自动从LB上摘掉，升级成功后自动加入集群；</p>
</li>
<li><p>事先需要有自动更新策略，分为若干次，每次数量/百分比可配置；</p>
</li>
<li><p>回滚是发布的逆过程，先从LB摘掉新版本，再升级老版本，这个过程一般时间比较长；</p>
</li>
<li><p>自动化要求高。</p>
</li>
</ul>
<h1 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h1><p>综上所述，三种方式均可以做到平滑式升级，在升级过程中服务仍然保持服务的连续性，升级对外界是无感知的。那生产上选择哪种部署方法最合适呢？这取决于哪种方法最适合你的业务和技术需求。如果你们运维自动化能力储备不够，肯定是越简单越好，建议蓝绿发布，如果业务对用户依赖很强，建议灰度发布。如果是K8S平台，滚动更新是现成的方案，建议先直接使用。</p>
<ul>
<li><p>蓝绿发布：两套环境交替升级，旧版本保留一定时间便于回滚。</p>
</li>
<li><p>灰度发布：根据比例将老版本升级，例如80%用户访问是老版本，20%用户访问是新版本。</p>
</li>
<li><p>滚动发布：按批次停止老版本实例，启动新版本实例。</p>
</li>
</ul>
<p>- END -</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>


<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>


<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"> 推荐阅读   </span><br><span class="line">  </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>


<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>


<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">[详解负载神器 LVS、Nginx及HAProxy工作原理](http:&#x2F;&#x2F;mp.weixin.qq.com&#x2F;s?__biz&#x3D;MzAwNTM5Njk3Mw&#x3D;&#x3D;&amp;mid&#x3D;2247495121&amp;idx&#x3D;1&amp;sn&#x3D;7b86b338d7aaeedbeb40224a1e02bc18&amp;chksm&#x3D;9b1fed53ac6864454e107618422a2539421584e30657dd81fcf8cecb2792f47cf8bff529f38f&amp;scene&#x3D;21#wechat_redirect)</span><br><span class="line"></span><br><span class="line">[服务端TCP连接的TIME\_WAIT过多问题的分析与解决](http:&#x2F;&#x2F;mp.weixin.qq.com&#x2F;s?__biz&#x3D;MzAwNTM5Njk3Mw&#x3D;&#x3D;&amp;mid&#x3D;2247495104&amp;idx&#x3D;1&amp;sn&#x3D;f211af4caf4668f13f96809810645a4a&amp;chksm&#x3D;9b1fed42ac686454420268f90b9184aca82a5764af0326aa339963d726b7ec95d27e382e99c7&amp;scene&#x3D;21#wechat_redirect)  </span><br><span class="line"></span><br><span class="line">[运维工程师必备技能：网络排错思路大讲解](http:&#x2F;&#x2F;mp.weixin.qq.com&#x2F;s?__biz&#x3D;MzAwNTM5Njk3Mw&#x3D;&#x3D;&amp;mid&#x3D;2247495096&amp;idx&#x3D;1&amp;sn&#x3D;370c368c584499a445e5723293145c9c&amp;chksm&#x3D;9b1fed3aac68642c0453d9c5559c79bb7acb532457171915c416a06f3c6529f61ba0f366fa36&amp;scene&#x3D;21#wechat_redirect)</span><br><span class="line"></span><br></pre></td></tr></table></figure>


<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzAwNTM5Njk3Mw==&mid=2247495068&idx=1&sn=585db2088cfb062907493d0d6d0239ae&chksm=9b1fed1eac6864085249762523411888c43302654eaf72285d27a88acbfd853981f549010cb1&scene=21#wechat_redirect">在项目实践中，进行了以下DevOps方案建设</a>  </p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzAwNTM5Njk3Mw==&mid=2247494305&idx=1&sn=35ed91a64a80571d138b016c5f42b11e&chksm=9b1fea23ac686335a9e0d9f045af6f79d941a09783e1d5670386aafe05340018373c9de1c938&scene=21#wechat_redirect">Linux系统常用命令速查手册</a><br><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzAwNTM5Njk3Mw==&mid=2247492267&idx=1&sn=41c9d6d020585ce4ecf9c00700bb0caf&chksm=9b1fe229ac686b3ff8c6064236106b7c404ad0bd5db65b01ea1e7889c8b61c1510052d7e7410&scene=21#wechat_redirect">Java 应用最常见的3个问题排查思路</a></p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzAwNTM5Njk3Mw==&mid=2247495019&idx=1&sn=85401c9f75e69ca450cf4e79647c902f&chksm=9b1fede9ac6864ff2a343ed4eee0d57a7f520292699e100fb12fc4497898ca4f424d27f3e272&scene=21#wechat_redirect">让运维简单高效，轻松搞定运维管理平台</a><img src=""></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>


<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>




<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">  </span><br><span class="line"></span><br></pre></td></tr></table></figure>


<p><img src=""></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">点亮，服务器三年不宕机![](data:image&#x2F;gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg&#x3D;&#x3D;)</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/other/" rel="tag">other</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
  </article>
  

  
  <nav class="page-nav">
    
    <a class="extend prev" rel="prev" href="/page/11/">上一页</a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/10/">10</a><a class="page-number" href="/page/11/">11</a><span class="page-number current">12</span><a class="page-number" href="/page/13/">13</a><a class="page-number" href="/page/14/">14</a><a class="extend next" rel="next" href="/page/13/">下一页</a>
  </nav>
  
</section>
</div>

      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2020
        <i class="ri-heart-fill heart_icon"></i> TzWind
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="Hexo"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" target="_blank" rel="noopener" href="http://www.baidu.com">百度</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/2019/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


    
  </div>
</body>

</html>